2013-05-22 11:49:27 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2013-06-24 11:50:51 +00:00
|
|
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
2013-05-22 11:49:27 +00:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
|
|
|
**
|
2013-06-24 11:50:51 +00:00
|
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
2013-05-22 11:49:27 +00:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
**
|
|
|
|
** 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, Digia gives you certain additional
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
|
|
** version 1.1, 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.
|
|
|
|
**
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qv4script_p.h"
|
|
|
|
#include "qv4mm_p.h"
|
|
|
|
#include "qv4functionobject_p.h"
|
|
|
|
#include "qv4function_p.h"
|
|
|
|
#include "qv4context_p.h"
|
|
|
|
#include "qv4debugging_p.h"
|
2013-09-05 11:22:23 +00:00
|
|
|
#include "qv4scopedvalue_p.h"
|
2013-05-22 11:49:27 +00:00
|
|
|
|
|
|
|
#include <private/qqmljsengine_p.h>
|
|
|
|
#include <private/qqmljslexer_p.h>
|
|
|
|
#include <private/qqmljsparser_p.h>
|
|
|
|
#include <private/qqmljsast_p.h>
|
2013-10-30 15:49:32 +00:00
|
|
|
#include <private/qqmlengine_p.h>
|
2013-05-22 11:49:27 +00:00
|
|
|
#include <qv4jsir_p.h>
|
|
|
|
#include <qv4codegen_p.h>
|
2014-04-03 07:38:48 +00:00
|
|
|
#include <private/qqmlcontextwrapper_p.h>
|
2013-05-22 11:49:27 +00:00
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
#include <QtCore/QString>
|
|
|
|
|
|
|
|
using namespace QV4;
|
|
|
|
|
2013-09-27 15:04:42 +00:00
|
|
|
QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, ObjectRef qml)
|
2014-05-06 07:23:59 +00:00
|
|
|
: FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false)
|
2013-05-22 12:30:57 +00:00
|
|
|
{
|
2014-04-29 09:20:12 +00:00
|
|
|
d()->qml = qml;
|
|
|
|
|
2014-04-05 18:23:20 +00:00
|
|
|
Q_ASSERT(scope->inUse());
|
2013-11-14 13:53:28 +00:00
|
|
|
|
2014-01-20 12:51:00 +00:00
|
|
|
setVTable(staticVTable());
|
2014-04-29 08:48:39 +00:00
|
|
|
d()->function = f;
|
2014-04-05 22:29:53 +00:00
|
|
|
if (function())
|
|
|
|
function()->compilationUnit->ref();
|
2014-04-30 18:55:41 +00:00
|
|
|
d()->needsActivation = function() ? function()->needsActivation() : false;
|
2013-11-14 13:53:28 +00:00
|
|
|
|
|
|
|
Scope s(scope);
|
|
|
|
ScopedValue protectThis(s, this);
|
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1));
|
2013-08-06 14:41:28 +00:00
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
d()->qmlContext = scope->d()->engine->currentContext()->newQmlContext(this, qml);
|
|
|
|
scope->d()->engine->popContext();
|
2013-08-06 14:41:28 +00:00
|
|
|
}
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
|
2013-09-27 15:04:42 +00:00
|
|
|
QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, ObjectRef qml)
|
2014-05-06 07:23:59 +00:00
|
|
|
: FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false)
|
2013-09-13 11:43:15 +00:00
|
|
|
{
|
2014-04-29 09:20:12 +00:00
|
|
|
d()->qml = qml;
|
|
|
|
|
2014-04-05 18:23:20 +00:00
|
|
|
Q_ASSERT(scope->inUse());
|
2013-11-14 13:53:28 +00:00
|
|
|
|
2014-01-20 12:51:00 +00:00
|
|
|
setVTable(staticVTable());
|
2014-04-30 18:55:41 +00:00
|
|
|
d()->needsActivation = false;
|
2013-11-14 13:53:28 +00:00
|
|
|
|
|
|
|
Scope s(scope);
|
|
|
|
ScopedValue protectThis(s, this);
|
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1));
|
2013-09-13 11:43:15 +00:00
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
d()->qmlContext = scope->d()->engine->currentContext()->newQmlContext(this, qml);
|
|
|
|
scope->d()->engine->popContext();
|
2013-09-13 11:43:15 +00:00
|
|
|
}
|
|
|
|
|
2013-09-20 06:21:42 +00:00
|
|
|
ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *)
|
2013-08-06 14:41:28 +00:00
|
|
|
{
|
|
|
|
ExecutionEngine *engine = that->engine();
|
2013-11-11 10:22:24 +00:00
|
|
|
CHECK_STACK_LIMITS(engine);
|
|
|
|
|
2013-09-20 06:21:42 +00:00
|
|
|
Scope scope(engine);
|
2013-08-06 14:41:28 +00:00
|
|
|
QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that);
|
2014-04-05 22:29:53 +00:00
|
|
|
if (!This->function())
|
2014-03-31 13:07:18 +00:00
|
|
|
return QV4::Encode::undefined();
|
2013-05-23 20:56:38 +00:00
|
|
|
|
2014-04-29 09:20:12 +00:00
|
|
|
CallContext *ctx = This->d()->qmlContext;
|
2014-03-03 20:00:30 +00:00
|
|
|
std::fill(ctx->locals, ctx->locals + ctx->function->varCount(), Primitive::undefinedValue());
|
2013-08-06 14:41:28 +00:00
|
|
|
engine->pushContext(ctx);
|
2014-04-05 22:29:53 +00:00
|
|
|
ScopedValue result(scope, This->function()->code(ctx, This->function()->codeData));
|
2013-08-06 14:41:28 +00:00
|
|
|
engine->popContext();
|
2013-05-23 20:56:38 +00:00
|
|
|
|
2013-09-20 06:21:42 +00:00
|
|
|
return result.asReturnedValue();
|
2013-08-06 14:41:28 +00:00
|
|
|
}
|
|
|
|
|
2013-11-02 15:30:26 +00:00
|
|
|
void QmlBindingWrapper::markObjects(Managed *m, ExecutionEngine *e)
|
2013-08-06 14:41:28 +00:00
|
|
|
{
|
|
|
|
QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m);
|
2014-04-29 09:20:12 +00:00
|
|
|
if (wrapper->d()->qml)
|
|
|
|
wrapper->d()->qml->mark(e);
|
2013-11-02 15:30:26 +00:00
|
|
|
FunctionObject::markObjects(m, e);
|
2014-04-29 09:20:12 +00:00
|
|
|
if (wrapper->d()->qmlContext)
|
|
|
|
wrapper->d()->qmlContext->mark(e);
|
2013-08-06 14:41:28 +00:00
|
|
|
}
|
2013-05-22 12:30:57 +00:00
|
|
|
|
2014-03-31 14:49:14 +00:00
|
|
|
static ReturnedValue signalParameterGetter(QV4::CallContext *ctx, uint parameterIndex)
|
2014-03-31 13:07:18 +00:00
|
|
|
{
|
2014-05-06 07:23:59 +00:00
|
|
|
QV4::CallContext *signalEmittingContext = ctx->d()->parent->asCallContext();
|
2014-03-31 14:49:14 +00:00
|
|
|
Q_ASSERT(signalEmittingContext);
|
|
|
|
return signalEmittingContext->argument(parameterIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Returned<FunctionObject> *QmlBindingWrapper::createQmlCallableForFunction(QQmlContextData *qmlContext, QObject *scopeObject, Function *runtimeFunction, const QList<QByteArray> &signalParameters, QString *error)
|
|
|
|
{
|
|
|
|
ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(qmlContext->engine);
|
2014-03-31 13:07:18 +00:00
|
|
|
QV4::Scope valueScope(engine);
|
|
|
|
QV4::ScopedObject qmlScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(engine->v8Engine, qmlContext, scopeObject));
|
|
|
|
QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, new (engine->memoryManager) QV4::QmlBindingWrapper(engine->rootContext, qmlScopeObject));
|
2014-03-31 14:49:14 +00:00
|
|
|
|
|
|
|
if (!signalParameters.isEmpty()) {
|
|
|
|
if (error)
|
|
|
|
QQmlPropertyCache::signalParameterStringForJS(qmlContext->engine, signalParameters, error);
|
|
|
|
QV4::ScopedProperty p(valueScope);
|
|
|
|
QV4::ScopedString s(valueScope);
|
|
|
|
int index = 0;
|
|
|
|
foreach (const QByteArray ¶m, signalParameters) {
|
|
|
|
p->setGetter(new (engine->memoryManager) QV4::IndexedBuiltinFunction(wrapper->context(), index++, signalParameterGetter));
|
|
|
|
p->setSetter(0);
|
|
|
|
s = engine->newString(QString::fromUtf8(param));
|
|
|
|
qmlScopeObject->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-31 13:07:18 +00:00
|
|
|
QV4::ScopedFunctionObject function(valueScope, QV4::FunctionObject::createScriptFunction(wrapper->context(), runtimeFunction));
|
|
|
|
return function->asReturned<FunctionObject>();
|
|
|
|
}
|
|
|
|
|
2014-04-04 10:22:00 +00:00
|
|
|
DEFINE_OBJECT_VTABLE(QmlBindingWrapper);
|
2013-05-23 20:56:38 +00:00
|
|
|
|
2014-04-29 09:20:12 +00:00
|
|
|
struct CompilationUnitHolder : public Object
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
{
|
2014-04-29 09:20:12 +00:00
|
|
|
struct Data : Object::Data {
|
|
|
|
QV4::CompiledData::CompilationUnit *unit;
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
QV4::CompiledData::CompilationUnit *unit;
|
|
|
|
} __data;
|
|
|
|
|
2014-04-30 18:50:42 +00:00
|
|
|
V4_OBJECT
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
|
|
|
|
CompilationUnitHolder(ExecutionEngine *engine, CompiledData::CompilationUnit *unit)
|
|
|
|
: Object(engine)
|
|
|
|
{
|
2014-04-29 09:20:12 +00:00
|
|
|
d()->unit = unit;
|
|
|
|
d()->unit->ref();
|
2014-01-20 12:51:00 +00:00
|
|
|
setVTable(staticVTable());
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
}
|
|
|
|
~CompilationUnitHolder()
|
|
|
|
{
|
2014-04-29 09:20:12 +00:00
|
|
|
d()->unit->deref();
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy(Managed *that)
|
|
|
|
{
|
|
|
|
static_cast<CompilationUnitHolder*>(that)->~CompilationUnitHolder();
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2014-01-20 12:51:00 +00:00
|
|
|
DEFINE_OBJECT_VTABLE(CompilationUnitHolder);
|
Fix crashes when running tst_qqmlecmascript::importScripts with aggressive gc
In the case of imported JavaScript files, it may happen that we parse the JS once
and then re-use it across different places where it is imported. That means we
parse and compile the JS once, keep the QV4::Script around and call it as a function
closure with different qml global objects (contexts), depending on where it is
imported from.
In this situation it is possible that the QV4::Script's run() is called once, a
new function object is created, we call it to return the JS library to "eval"
itself into the qml scope and afterwards it may happen that the function object
is garbage collected. It is at this point possible that the compilation unit's
refcount therefore also drops to zero, and thus subsequent calls to
QV4::Script::run() that create new QQmlBinding objects will access a dangling
compilationUnit pointer.
This patch fixes that by making QV4::Script - which is holding a QV4::Function
pointer - also have a persistent, which maintainers a refcount on the
compilation unit. If the script dies, the persistent will get collected and
the last deref will delete the unit. A script can however outlive the engine,
but PersistentValue has the mechanism built in to free itself on engine
destruction, which will also deref the unit accordingly.
Change-Id: I0a7f4e64497bde423ffa55c705af55cdb7d29cf2
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2013-08-26 13:25:47 +00:00
|
|
|
|
2013-09-29 19:20:09 +00:00
|
|
|
Script::Script(ExecutionEngine *v4, ObjectRef qml, CompiledData::CompilationUnit *compilationUnit)
|
|
|
|
: line(0), column(0), scope(v4->rootContext), strictMode(false), inheritContext(true), parsed(false)
|
|
|
|
, qml(qml.asReturnedValue()), vmFunction(0), parseAsBinding(true)
|
|
|
|
{
|
|
|
|
parsed = true;
|
|
|
|
|
|
|
|
if (compilationUnit) {
|
|
|
|
vmFunction = compilationUnit->linkToEngine(v4);
|
|
|
|
Q_ASSERT(vmFunction);
|
|
|
|
Scope valueScope(v4);
|
2013-09-30 20:41:12 +00:00
|
|
|
ScopedValue holder(valueScope, new (v4->memoryManager) CompilationUnitHolder(v4, compilationUnit));
|
2014-01-27 13:58:52 +00:00
|
|
|
compilationUnitHolder = holder.asReturnedValue();
|
2013-09-29 19:20:09 +00:00
|
|
|
} else
|
|
|
|
vmFunction = 0;
|
|
|
|
}
|
|
|
|
|
2013-08-09 14:45:02 +00:00
|
|
|
Script::~Script()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-05-22 11:49:27 +00:00
|
|
|
void Script::parse()
|
|
|
|
{
|
2013-06-25 09:00:31 +00:00
|
|
|
if (parsed)
|
|
|
|
return;
|
|
|
|
|
2013-05-22 11:49:27 +00:00
|
|
|
using namespace QQmlJS;
|
|
|
|
|
2013-05-23 20:56:38 +00:00
|
|
|
parsed = true;
|
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
ExecutionEngine *v4 = scope->d()->engine;
|
2013-09-13 19:54:21 +00:00
|
|
|
Scope valueScope(v4);
|
2013-05-22 12:30:57 +00:00
|
|
|
|
|
|
|
MemoryManager::GCBlocker gcBlocker(v4->memoryManager);
|
2013-05-22 11:49:27 +00:00
|
|
|
|
2014-02-14 12:58:40 +00:00
|
|
|
IR::Module module(v4->debugger != 0);
|
2013-05-22 11:49:27 +00:00
|
|
|
|
2013-05-23 20:56:38 +00:00
|
|
|
QQmlJS::Engine ee, *engine = ⅇ
|
|
|
|
Lexer lexer(engine);
|
2013-06-02 20:56:59 +00:00
|
|
|
lexer.setCode(sourceCode, line, parseAsBinding);
|
2013-05-23 20:56:38 +00:00
|
|
|
Parser parser(engine);
|
|
|
|
|
|
|
|
const bool parsed = parser.parseProgram();
|
|
|
|
|
|
|
|
foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) {
|
|
|
|
if (m.isError()) {
|
2013-08-08 14:59:32 +00:00
|
|
|
scope->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn);
|
2013-10-21 07:50:27 +00:00
|
|
|
return;
|
2013-05-23 20:56:38 +00:00
|
|
|
} else {
|
|
|
|
qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn
|
|
|
|
<< ": warning: " << m.message;
|
2013-05-22 11:49:27 +00:00
|
|
|
}
|
2013-05-23 20:56:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parsed) {
|
|
|
|
using namespace AST;
|
|
|
|
Program *program = AST::cast<Program *>(parser.rootNode());
|
|
|
|
if (!program) {
|
|
|
|
// if parsing was successful, and we have no program, then
|
|
|
|
// we're done...:
|
|
|
|
return;
|
2013-05-22 11:49:27 +00:00
|
|
|
}
|
2013-05-23 20:56:38 +00:00
|
|
|
|
|
|
|
QStringList inheritedLocals;
|
2014-03-05 15:24:56 +00:00
|
|
|
if (inheritContext) {
|
|
|
|
CallContext *ctx = scope->asCallContext();
|
|
|
|
if (ctx) {
|
|
|
|
for (String * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i)
|
|
|
|
inheritedLocals.append(*i ? (*i)->toQString() : QString());
|
|
|
|
}
|
|
|
|
}
|
2013-05-23 20:56:38 +00:00
|
|
|
|
2013-08-08 14:59:32 +00:00
|
|
|
RuntimeCodegen cg(scope, strictMode);
|
2013-10-24 12:51:02 +00:00
|
|
|
cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals);
|
2013-10-21 07:50:27 +00:00
|
|
|
if (v4->hasException)
|
|
|
|
return;
|
|
|
|
|
2013-09-01 11:11:00 +00:00
|
|
|
QV4::Compiler::JSUnitGenerator jsGenerator(&module);
|
2013-10-30 15:49:32 +00:00
|
|
|
QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator));
|
2013-05-23 20:56:38 +00:00
|
|
|
if (inheritContext)
|
|
|
|
isel->setUseFastLookups(false);
|
2013-08-14 08:17:37 +00:00
|
|
|
QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile();
|
|
|
|
vmFunction = compilationUnit->linkToEngine(v4);
|
2013-09-26 20:07:27 +00:00
|
|
|
ScopedValue holder(valueScope, new (v4->memoryManager) CompilationUnitHolder(v4, compilationUnit));
|
2014-01-27 13:58:52 +00:00
|
|
|
compilationUnitHolder = holder.asReturnedValue();
|
2013-05-22 11:49:27 +00:00
|
|
|
}
|
2013-05-23 20:56:38 +00:00
|
|
|
|
2013-09-13 19:54:21 +00:00
|
|
|
if (!vmFunction) {
|
2013-05-22 12:30:57 +00:00
|
|
|
// ### FIX file/line number
|
2013-11-01 11:38:32 +00:00
|
|
|
Scoped<Object> error(valueScope, v4->newSyntaxErrorObject(QStringLiteral("Syntax error")));
|
2013-11-21 15:41:32 +00:00
|
|
|
v4->currentContext()->throwError(error);
|
2013-09-13 19:54:21 +00:00
|
|
|
}
|
2013-05-22 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 13:27:01 +00:00
|
|
|
ReturnedValue Script::run()
|
2013-05-22 11:49:27 +00:00
|
|
|
{
|
2013-10-16 12:03:48 +00:00
|
|
|
struct ContextStateSaver {
|
|
|
|
ExecutionContext *savedContext;
|
|
|
|
bool strictMode;
|
|
|
|
Lookup *lookups;
|
|
|
|
CompiledData::CompilationUnit *compilationUnit;
|
|
|
|
|
|
|
|
ContextStateSaver(ExecutionContext *context)
|
|
|
|
: savedContext(context)
|
2014-05-06 07:23:59 +00:00
|
|
|
, strictMode(context->d()->strictMode)
|
|
|
|
, lookups(context->d()->lookups)
|
|
|
|
, compilationUnit(context->d()->compilationUnit)
|
2013-10-16 12:03:48 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
~ContextStateSaver()
|
|
|
|
{
|
2014-05-06 07:23:59 +00:00
|
|
|
savedContext->d()->strictMode = strictMode;
|
|
|
|
savedContext->d()->lookups = lookups;
|
|
|
|
savedContext->d()->compilationUnit = compilationUnit;
|
2013-10-16 12:03:48 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-23 20:56:38 +00:00
|
|
|
if (!parsed)
|
2013-05-22 12:30:57 +00:00
|
|
|
parse();
|
2013-05-23 20:56:38 +00:00
|
|
|
if (!vmFunction)
|
2013-09-12 13:27:01 +00:00
|
|
|
return Encode::undefined();
|
2013-05-22 11:49:27 +00:00
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
QV4::ExecutionEngine *engine = scope->d()->engine;
|
2013-09-11 12:47:34 +00:00
|
|
|
QV4::Scope valueScope(engine);
|
2013-05-22 11:49:27 +00:00
|
|
|
|
2013-09-17 16:16:35 +00:00
|
|
|
if (qml.isUndefined()) {
|
2013-05-23 20:56:38 +00:00
|
|
|
TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
|
2013-05-22 14:53:35 +00:00
|
|
|
|
2013-10-16 12:03:48 +00:00
|
|
|
ExecutionContextSaver ctxSaver(scope);
|
|
|
|
ContextStateSaver stateSaver(scope);
|
2014-05-06 07:23:59 +00:00
|
|
|
scope->d()->strictMode = vmFunction->isStrict();
|
|
|
|
scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups;
|
|
|
|
scope->d()->compilationUnit = vmFunction->compilationUnit;
|
2013-05-22 14:53:35 +00:00
|
|
|
|
2013-10-16 12:03:48 +00:00
|
|
|
return vmFunction->code(scope, vmFunction->codeData);
|
2013-05-22 14:53:35 +00:00
|
|
|
} else {
|
2013-09-23 13:52:10 +00:00
|
|
|
ScopedObject qmlObj(valueScope, qml.value());
|
2013-09-27 15:04:42 +00:00
|
|
|
FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qmlObj);
|
2013-09-11 12:36:01 +00:00
|
|
|
ScopedCallData callData(valueScope, 0);
|
2013-09-25 10:24:36 +00:00
|
|
|
callData->thisObject = Primitive::undefinedValue();
|
2013-09-05 11:22:23 +00:00
|
|
|
return f->call(callData);
|
2013-05-22 14:53:35 +00:00
|
|
|
}
|
2013-05-22 12:30:57 +00:00
|
|
|
}
|
2013-05-22 11:49:27 +00:00
|
|
|
|
2013-05-22 12:30:57 +00:00
|
|
|
Function *Script::function()
|
|
|
|
{
|
2013-05-23 20:56:38 +00:00
|
|
|
if (!parsed)
|
|
|
|
parse();
|
|
|
|
return vmFunction;
|
|
|
|
}
|
|
|
|
|
2014-03-18 17:21:18 +00:00
|
|
|
QV4::CompiledData::CompilationUnit *Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors)
|
2013-09-29 19:20:09 +00:00
|
|
|
{
|
|
|
|
using namespace QQmlJS;
|
|
|
|
using namespace QQmlJS::AST;
|
|
|
|
|
|
|
|
QQmlJS::Engine ee;
|
|
|
|
QQmlJS::Lexer lexer(&ee);
|
|
|
|
lexer.setCode(source, /*line*/1, /*qml mode*/true);
|
|
|
|
QQmlJS::Parser parser(&ee);
|
|
|
|
|
|
|
|
parser.parseProgram();
|
|
|
|
|
|
|
|
QList<QQmlError> errors;
|
|
|
|
|
|
|
|
foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) {
|
|
|
|
if (m.isWarning()) {
|
|
|
|
qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QQmlError error;
|
|
|
|
error.setUrl(url);
|
|
|
|
error.setDescription(m.message);
|
|
|
|
error.setLine(m.loc.startLine);
|
|
|
|
error.setColumn(m.loc.startColumn);
|
|
|
|
errors << error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!errors.isEmpty()) {
|
|
|
|
if (reportedErrors)
|
|
|
|
*reportedErrors << errors;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Program *program = AST::cast<Program *>(parser.rootNode());
|
|
|
|
if (!program) {
|
|
|
|
// if parsing was successful, and we have no program, then
|
|
|
|
// we're done...:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-30 09:08:28 +00:00
|
|
|
QQmlJS::Codegen cg(/*strict mode*/false);
|
2014-03-18 17:21:18 +00:00
|
|
|
cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::Codegen::EvalCode);
|
2014-03-12 15:55:06 +00:00
|
|
|
errors = cg.qmlErrors();
|
2013-10-30 09:08:28 +00:00
|
|
|
if (!errors.isEmpty()) {
|
2013-09-29 19:20:09 +00:00
|
|
|
if (reportedErrors)
|
2014-03-12 15:55:06 +00:00
|
|
|
*reportedErrors << errors;
|
2013-09-29 19:20:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-18 17:21:18 +00:00
|
|
|
QScopedPointer<EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, module, unitGenerator));
|
2013-09-29 19:20:09 +00:00
|
|
|
isel->setUseFastLookups(false);
|
2014-03-18 17:21:18 +00:00
|
|
|
return isel->compile(/*generate unit data*/false);
|
2013-09-29 19:20:09 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 20:37:41 +00:00
|
|
|
ReturnedValue Script::qmlBinding()
|
2013-05-23 20:56:38 +00:00
|
|
|
{
|
|
|
|
if (!parsed)
|
|
|
|
parse();
|
2014-05-06 07:23:59 +00:00
|
|
|
ExecutionEngine *v4 = scope->d()->engine;
|
2013-09-23 13:52:10 +00:00
|
|
|
Scope valueScope(v4);
|
|
|
|
ScopedObject qmlObj(valueScope, qml.value());
|
2013-09-27 15:04:42 +00:00
|
|
|
ScopedObject v(valueScope, new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qmlObj));
|
2013-09-25 13:24:50 +00:00
|
|
|
return v.asReturnedValue();
|
2013-05-22 11:49:27 +00:00
|
|
|
}
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, ObjectRef scopeObject)
|
2013-05-22 14:26:45 +00:00
|
|
|
{
|
2013-09-12 13:27:01 +00:00
|
|
|
QV4::Scope scope(engine);
|
2013-05-22 14:26:45 +00:00
|
|
|
QV4::Script qmlScript(engine, scopeObject, script, QString());
|
|
|
|
|
2013-11-21 15:41:32 +00:00
|
|
|
QV4::ExecutionContext *ctx = engine->currentContext();
|
2013-10-21 07:50:27 +00:00
|
|
|
qmlScript.parse();
|
|
|
|
QV4::ScopedValue result(scope);
|
|
|
|
if (!scope.engine->hasException)
|
|
|
|
result = qmlScript.run();
|
|
|
|
if (scope.engine->hasException) {
|
2013-09-11 11:23:21 +00:00
|
|
|
ctx->catchException();
|
2013-10-21 07:50:27 +00:00
|
|
|
return Encode::undefined();
|
2013-05-22 14:26:45 +00:00
|
|
|
}
|
2013-10-21 07:50:27 +00:00
|
|
|
return result.asReturnedValue();
|
2013-05-22 14:26:45 +00:00
|
|
|
}
|