Commit 9dc9674c authored by Christian Kamm's avatar Christian Kamm

QmlJS: Share Context.

Previously Context was not entirely thread safe and had to be
copied locally. Now it is thread safe and its lifetime
managed by QSharedPointer.

The non-safe parts were moved into ScopeChain in a previous commit.

Change-Id: I851a93de85cbd6391dbea0fe33b011e2e093addb
Reviewed-on: http://codereview.qt.nokia.com/1695Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@nokia.com>
parent f87dc619
......@@ -97,7 +97,7 @@ Interpreter::ObjectValue *Bind::findQmlObject(AST::Node *node) const
}
bool Bind::usesQmlPrototype(ObjectValue *prototype,
const Context *context) const
const ContextPtr &context) const
{
if (!prototype)
return false;
......
......@@ -62,7 +62,7 @@ public:
Interpreter::ObjectValue *findQmlObject(AST::Node *node) const;
bool usesQmlPrototype(Interpreter::ObjectValue *prototype,
const Interpreter::Context *context) const;
const Interpreter::ContextPtr &context) const;
Interpreter::ObjectValue *findAttachedJSScope(AST::Node *node) const;
bool isGroupedPropertyBinding(AST::Node *node) const;
......
......@@ -365,10 +365,10 @@ private:
} // end of anonymous namespace
Check::Check(Document::Ptr doc, const Context *context)
Check::Check(Document::Ptr doc, const ContextPtr &context)
: _doc(doc)
, _context(*context)
, _scopeChain(doc, &_context)
, _context(context)
, _scopeChain(doc, _context)
, _scopeBuilder(&_scopeChain)
, _options(WarnDangerousNonStrictEqualityChecks | WarnBlocks | WarnWith
| WarnVoid | WarnCommaExpression | WarnExpressionStatement
......@@ -471,14 +471,14 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
bool typeError = false;
const SourceLocation typeErrorLocation = fullLocationForQualifiedId(typeId);
const ObjectValue *prototype = _context.lookupType(_doc.data(), typeId);
const ObjectValue *prototype = _context->lookupType(_doc.data(), typeId);
if (!prototype) {
typeError = true;
if (_options & ErrCheckTypeErrors)
error(typeErrorLocation,
Check::tr("unknown type"));
} else {
PrototypeIterator iter(prototype, &_context);
PrototypeIterator iter(prototype, _context);
QList<const ObjectValue *> prototypes = iter.all();
if (iter.error() != PrototypeIterator::NoError)
typeError = true;
......@@ -610,7 +610,7 @@ bool Check::visit(IdentifierExpression *ast)
if (!_lastValue)
error(ast->identifierToken, tr("unknown identifier"));
if (const Reference *ref = value_cast<const Reference *>(_lastValue)) {
_lastValue = _context.lookupReference(ref);
_lastValue = _context->lookupReference(ref);
if (!_lastValue)
error(ast->identifierToken, tr("could not resolve"));
}
......@@ -635,7 +635,7 @@ bool Check::visit(FieldMemberExpression *ast)
_lastValue = 0;
return false;
}
_lastValue = obj->lookupMember(ast->name->asString(), &_context);
_lastValue = obj->lookupMember(ast->name->asString(), _context);
if (!_lastValue)
error(ast->identifierToken, tr("unknown member"));
return false;
......@@ -878,7 +878,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
// global lookup for first part of id
const Value *value = 0;
for (int i = scopeObjects.size() - 1; i >= 0; --i) {
value = scopeObjects[i]->lookupMember(propertyName, &_context);
value = scopeObjects[i]->lookupMember(propertyName, _context);
if (value)
break;
}
......@@ -894,7 +894,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
// resolve references
if (const Reference *ref = value->asReference())
value = _context.lookupReference(ref);
value = _context->lookupReference(ref);
// member lookup
const UiQualifiedId *idPart = id;
......@@ -915,7 +915,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
idPart = idPart->next;
propertyName = idPart->name->asString();
value = objectValue->lookupMember(propertyName, &_context);
value = objectValue->lookupMember(propertyName, _context);
if (! value) {
error(idPart->identifierToken,
Check::tr("'%1' is not a member of '%2'").arg(
......
......@@ -54,7 +54,7 @@ class QMLJS_EXPORT Check: protected AST::Visitor
public:
// prefer taking root scope chain?
Check(Document::Ptr doc, const Interpreter::Context *context);
Check(Document::Ptr doc, const Interpreter::ContextPtr &context);
virtual ~Check();
QList<DiagnosticMessage> operator()();
......@@ -128,7 +128,7 @@ private:
Document::Ptr _doc;
Interpreter::Context _context;
Interpreter::ContextPtr _context;
Interpreter::ScopeChain _scopeChain;
ScopeBuilder _scopeBuilder;
......
......@@ -38,6 +38,13 @@ using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;
ContextPtr Context::create(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports)
{
QSharedPointer<Context> result(new Context(snapshot, valueOwner, imports));
result->_ptr = result;
return result;
}
Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports)
: _snapshot(snapshot),
_valueOwner(valueOwner),
......@@ -49,6 +56,11 @@ Context::~Context()
{
}
ContextPtr Context::ptr() const
{
return _ptr.toStrongRef();
}
// the values is only guaranteed to live as long as the context
ValueOwner *Context::valueOwner() const
{
......@@ -117,7 +129,7 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QString
const Value *Context::lookupReference(const Value *value) const
{
ReferenceContext refContext(this);
ReferenceContext refContext(ptr());
return refContext.lookupReference(value);
}
......@@ -137,7 +149,7 @@ QString Context::defaultPropertyName(const ObjectValue *object) const
return QString();
}
ReferenceContext::ReferenceContext(const Context *context)
ReferenceContext::ReferenceContext(const ContextPtr &context)
: m_context(context)
{}
......@@ -157,12 +169,12 @@ const Value *ReferenceContext::lookupReference(const Value *value)
return v;
}
const Context *ReferenceContext::context() const
const ContextPtr &ReferenceContext::context() const
{
return m_context;
}
ReferenceContext::operator const Context *() const
ReferenceContext::operator const ContextPtr &() const
{
return m_context;
}
......@@ -45,18 +45,22 @@ class Snapshot;
namespace Interpreter {
class Context;
typedef QSharedPointer<const Context> ContextPtr;
// shared among threads, completely threadsafe
// currently also safe to copy, but will be deprecated
class QMLJS_EXPORT Context
{
Q_DISABLE_COPY(Context)
public:
typedef QSharedPointer<Context> Ptr;
typedef QHash<const Document *, QSharedPointer<const Imports> > ImportsPerDocument;
// Context takes ownership of valueOwner
Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports);
static ContextPtr create(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports);
~Context();
ContextPtr ptr() const;
ValueOwner *valueOwner() const;
Snapshot snapshot() const;
......@@ -70,9 +74,13 @@ public:
QString defaultPropertyName(const ObjectValue *object) const;
private:
// Context takes ownership of valueOwner
Context(const Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports);
Snapshot _snapshot;
QSharedPointer<ValueOwner> _valueOwner;
ImportsPerDocument _imports;
QWeakPointer<const Context> _ptr;
};
// for looking up references
......@@ -80,15 +88,15 @@ class QMLJS_EXPORT ReferenceContext
{
public:
// implicit conversion ok
ReferenceContext(const Context *context);
ReferenceContext(const ContextPtr &context);
const Value *lookupReference(const Value *value);
const Context *context() const;
operator const Context *() const;
const ContextPtr &context() const;
operator const ContextPtr &() const;
private:
const Context *m_context;
const ContextPtr &m_context;
QList<const Reference *> m_references;
};
......
......@@ -165,7 +165,7 @@ protected:
private:
QmlJS::Document::Ptr _doc;
Interpreter::ValueOwner *_valueOwner;
const Interpreter::Context *_context;
Interpreter::ContextPtr _context;
const Interpreter::ScopeChain *_scopeChain;
const Interpreter::ObjectValue *_scope;
const Interpreter::Value *_result;
......
......@@ -888,7 +888,7 @@ const Value *ObjectValue::lookupMember(const QString &name, const Context *conte
}
}
if (examinePrototypes) {
if (examinePrototypes && context) {
PrototypeIterator iter(this, context);
iter.next(); // skip this
while (iter.hasNext()) {
......@@ -913,6 +913,16 @@ PrototypeIterator::PrototypeIterator(const ObjectValue *start, const Context *co
m_prototypes.reserve(10);
}
PrototypeIterator::PrototypeIterator(const ObjectValue *start, const ContextPtr &context)
: m_current(0)
, m_next(start)
, m_context(context.data())
, m_error(NoError)
{
if (start)
m_prototypes.reserve(10);
}
bool PrototypeIterator::hasNext()
{
if (m_next)
......@@ -1422,14 +1432,14 @@ void ConvertToNumber::visit(const StringValue *)
void ConvertToNumber::visit(const ObjectValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) {
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", ContextPtr()))) {
_result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number?
}
}
void ConvertToNumber::visit(const FunctionValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) {
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", ContextPtr()))) {
_result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number?
}
}
......@@ -1483,14 +1493,14 @@ void ConvertToString::visit(const StringValue *value)
void ConvertToString::visit(const ObjectValue *object)
{
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) {
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", ContextPtr()))) {
_result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string?
}
}
void ConvertToString::visit(const FunctionValue *object)
{
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) {
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", ContextPtr()))) {
_result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string?
}
}
......
......@@ -75,6 +75,7 @@ class Imports;
class TypeScope;
class JSImportScope;
class Context;
typedef QSharedPointer<const Context> ContextPtr;
class ReferenceContext;
typedef QList<const Value *> ValueList;
......@@ -334,6 +335,8 @@ public:
const Value *prototype() const;
// prototypes may form a cycle: use PrototypeIterator!
const ObjectValue *prototype(const Context *context) const;
const ObjectValue *prototype(const ContextPtr &context) const
{ return prototype(context.data()); }
void setPrototype(const Value *prototype);
virtual void processMembers(MemberProcessor *processor) const;
......@@ -344,6 +347,10 @@ public:
virtual const Value *lookupMember(const QString &name, const Context *context,
const ObjectValue **foundInObject = 0,
bool examinePrototypes = true) const;
const Value *lookupMember(const QString &name, const ContextPtr &context,
const ObjectValue **foundInObject = 0,
bool examinePrototypes = true) const
{ return lookupMember(name, context.data(), foundInObject, examinePrototypes); }
// Value interface
virtual const ObjectValue *asObjectValue() const;
......@@ -372,6 +379,7 @@ public:
};
PrototypeIterator(const ObjectValue *start, const Context *context);
PrototypeIterator(const ObjectValue *start, const ContextPtr &context);
bool hasNext();
const ObjectValue *peekNext();
......
......@@ -130,19 +130,19 @@ Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const Libra
}
}
Context Link::operator()(QHash<QString, QList<DiagnosticMessage> > *messages)
ContextPtr Link::operator()(QHash<QString, QList<DiagnosticMessage> > *messages)
{
Q_D(Link);
d->allDiagnosticMessages = messages;
return Context(d->snapshot, d->valueOwner, linkImports());
return Context::create(d->snapshot, d->valueOwner, linkImports());
}
Context Link::operator()(const Document::Ptr &doc, QList<DiagnosticMessage> *messages)
ContextPtr Link::operator()(const Document::Ptr &doc, QList<DiagnosticMessage> *messages)
{
Q_D(Link);
d->doc = doc;
d->diagnosticMessages = messages;
return Context(d->snapshot, d->valueOwner, linkImports());
return Context::create(d->snapshot, d->valueOwner, linkImports());
}
Link::~Link()
......
......@@ -58,11 +58,11 @@ public:
Link(const Snapshot &snapshot, const QStringList &importPaths, const LibraryInfo &builtins);
// Link all documents in snapshot, collecting all diagnostic messages (if messages != 0)
Interpreter::Context operator()(QHash<QString, QList<DiagnosticMessage> > *messages = 0);
Interpreter::ContextPtr operator()(QHash<QString, QList<DiagnosticMessage> > *messages = 0);
// Link all documents in snapshot, appending the diagnostic messages
// for 'doc' in 'messages'
Interpreter::Context operator()(const Document::Ptr &doc, QList<DiagnosticMessage> *messages);
Interpreter::ContextPtr operator()(const Document::Ptr &doc, QList<DiagnosticMessage> *messages);
~Link();
......
......@@ -37,6 +37,7 @@
#include "qmljsmodelmanagerinterface.h"
#include "qmljsevaluate.h"
#include "qmljsscopechain.h"
#include "qmljscontext.h"
using namespace QmlJS;
......@@ -48,25 +49,25 @@ public:
, context(Link(snapshot,
ModelManagerInterface::instance()->importPaths(),
ModelManagerInterface::instance()->builtins(doc))())
, scopeChain(doc, &context)
, scopeChain(doc, context)
{
ScopeBuilder scopeBuilder(&scopeChain);
scopeBuilder.push(path);
}
LookupContextData(Document::Ptr doc,
const Interpreter::Context &context,
const Interpreter::ContextPtr &context,
const QList<AST::Node *> &path)
: doc(doc)
, context(context)
, scopeChain(doc, &context)
, scopeChain(doc, context)
{
ScopeBuilder scopeBuilder(&scopeChain);
scopeBuilder.push(path);
}
Document::Ptr doc;
Interpreter::Context context;
Interpreter::ContextPtr context;
Interpreter::ScopeChain scopeChain;
};
......@@ -76,7 +77,7 @@ LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const
}
LookupContext::LookupContext(const Document::Ptr doc,
const Interpreter::Context &context,
const Interpreter::ContextPtr &context,
const QList<AST::Node *> &path)
: d(new LookupContextData(doc, context, path))
{
......@@ -93,7 +94,7 @@ LookupContext::Ptr LookupContext::create(Document::Ptr doc, const Snapshot &snap
}
LookupContext::Ptr LookupContext::create(const Document::Ptr doc,
const Interpreter::Context &context,
const Interpreter::ContextPtr &context,
const QList<AST::Node *> &path)
{
Ptr ptr(new LookupContext(doc, context, path));
......@@ -113,19 +114,19 @@ Document::Ptr LookupContext::document() const
Snapshot LookupContext::snapshot() const
{
return d->context.snapshot();
return d->context->snapshot();
}
// the engine is only guaranteed to live as long as the LookupContext
Interpreter::ValueOwner *LookupContext::valueOwner() const
{
return d->context.valueOwner();
return d->context->valueOwner();
}
// the context is only guaranteed to live as long as the LookupContext
const Interpreter::Context *LookupContext::context() const
const Interpreter::ContextPtr &LookupContext::context() const
{
return &d->context;
return d->context;
}
const Interpreter::ScopeChain &LookupContext::scopeChain() const
......
......@@ -34,7 +34,6 @@
#define QMLJSLOOKUPCONTEXT_H
#include "qmljsdocument.h"
#include "qmljscontext.h"
#include "parser/qmljsastfwd_p.h"
#include <QtCore/QSharedPointer>
......@@ -47,6 +46,8 @@ class LookupContextData;
namespace Interpreter {
class Value;
class Context;
typedef QSharedPointer<const Context> ContextPtr;
class ValueOwner;
class ScopeChain;
}
......@@ -56,7 +57,7 @@ class QMLJS_EXPORT LookupContext
LookupContext(const Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path);
LookupContext(const Document::Ptr doc,
const Interpreter::Context &context,
const Interpreter::ContextPtr &context,
const QList<AST::Node *> &path);
public:
......@@ -68,7 +69,7 @@ public:
static Ptr create(const Document::Ptr doc, const Snapshot &snapshot,
const QList<AST::Node *> &path);
static Ptr create(const Document::Ptr doc,
const Interpreter::Context &context,
const Interpreter::ContextPtr &context,
const QList<AST::Node *> &path);
const Interpreter::Value *evaluate(AST::Node *node) const;
......@@ -76,7 +77,7 @@ public:
Document::Ptr document() const;
Snapshot snapshot() const;
Interpreter::ValueOwner *valueOwner() const;
const Interpreter::Context *context() const;
const Interpreter::ContextPtr &context() const;
const Interpreter::ScopeChain &scopeChain() const;
private:
......
......@@ -224,7 +224,7 @@ const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id)
}
const ObjectValue *ScopeBuilder::isPropertyChangesObject(const Context *context,
const ObjectValue *ScopeBuilder::isPropertyChangesObject(const ContextPtr &context,
const ObjectValue *object)
{
PrototypeIterator iter(object, context);
......
......@@ -42,6 +42,7 @@ namespace QmlJS {
namespace Interpreter {
class QmlComponentChain;
class Context;
typedef QSharedPointer<const Context> ContextPtr;
class ObjectValue;
class Value;
class ScopeChain;
......@@ -61,7 +62,7 @@ public:
void push(const QList<AST::Node *> &nodes);
void pop();
static const Interpreter::ObjectValue *isPropertyChangesObject(const Interpreter::Context *context, const Interpreter::ObjectValue *object);
static const Interpreter::ObjectValue *isPropertyChangesObject(const Interpreter::ContextPtr &context, const Interpreter::ObjectValue *object);
private:
void setQmlScopeObject(AST::Node *node);
......
......@@ -62,7 +62,7 @@ void QmlComponentChain::addInstantiatingComponent(const QmlComponentChain *compo
}
ScopeChain::ScopeChain(const Document::Ptr &document, const Context *context)
ScopeChain::ScopeChain(const Document::Ptr &document, const ContextPtr &context)
: m_document(document)
, m_context(context)
, m_globalScope(0)
......@@ -78,7 +78,7 @@ Document::Ptr ScopeChain::document() const
return m_document;
}
const Context *ScopeChain::context() const
const ContextPtr &ScopeChain::context() const
{
return m_context;
}
......
......@@ -73,10 +73,10 @@ private:
class QMLJS_EXPORT ScopeChain
{
public:
explicit ScopeChain(const Document::Ptr &document, const Context *context);
explicit ScopeChain(const Document::Ptr &document, const ContextPtr &context);
Document::Ptr document() const;
const Context *context() const;
const ContextPtr &context() const;
const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const;
......@@ -108,7 +108,7 @@ private:
Document::Ptr m_document;
const Context *m_context;
ContextPtr m_context;
const ObjectValue *m_globalScope;
QSharedPointer<const QmlComponentChain> m_qmlComponentScope;
......
......@@ -336,15 +336,15 @@ public:
, m_doc(doc)
, m_link(snapshot, importPaths,
QmlJS::ModelManagerInterface::instance()->builtins(doc))
, m_context(new Interpreter::Context(m_link(doc, &m_diagnosticLinkMessages)))
, m_context(m_link(doc, &m_diagnosticLinkMessages))
, m_scopeChain(doc, m_context)
, m_scopeBuilder(&m_scopeChain)
{
m_lookupContext = LookupContext::create(doc, *m_context, QList<AST::Node*>());
m_lookupContext = LookupContext::create(doc, m_context, QList<AST::Node*>());
}
~ReadingContext()
{ delete m_context; }
{}
Document::Ptr doc() const
{ return m_doc; }
......@@ -385,7 +385,7 @@ public:
minorVersion = ComponentVersion::NoVersion;
const Interpreter::Imports *imports = m_lookupContext->context()->imports(m_lookupContext->document().data());
Interpreter::ImportInfo importInfo = imports->info(fullTypeName, m_context);
Interpreter::ImportInfo importInfo = imports->info(fullTypeName, m_context.data());
if (importInfo.isValid() && importInfo.type() == Interpreter::ImportInfo::LibraryImport) {
QString name = importInfo.name().replace("\\", ".");
majorVersion = importInfo.version().majorVersion();
......@@ -632,7 +632,7 @@ private:
Document::Ptr m_doc;
Link m_link;
QList<DiagnosticMessage> m_diagnosticLinkMessages;
Interpreter::Context *m_context;
Interpreter::ContextPtr m_context;
LookupContext::Ptr m_lookupContext;
Interpreter::ScopeChain m_scopeChain;