Commit f87dc619 authored by Christian Kamm's avatar Christian Kamm

QmlJS: Split Context and ScopeChain.

Context is created by Link and has information about imports
for all Documents in a Snapshot.

ScopeChain represents how lookup is done at a specific place in
a Document.

Change-Id: I874102d57bbaf1a497fa3f27633bed6ee75dcf10
Reviewed-on: http://codereview.qt.nokia.com/1694Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@nokia.com>
parent ed1321a4
......@@ -30,7 +30,8 @@ HEADERS += \
$$PWD/qmljstypedescriptionreader.h \
$$PWD/qmljsscopeastpath.h \
$$PWD/qmljsvalueowner.h \
$$PWD/qmljscontext.h
$$PWD/qmljscontext.h \
$$PWD/qmljsscopechain.h
SOURCES += \
$$PWD/qmljsbind.cpp \
......@@ -52,7 +53,8 @@ SOURCES += \
$$PWD/qmljstypedescriptionreader.cpp \
$$PWD/qmljsscopeastpath.cpp \
$$PWD/qmljsvalueowner.cpp \
$$PWD/qmljscontext.cpp
$$PWD/qmljscontext.cpp \
$$PWD/qmljsscopechain.cpp
RESOURCES += \
$$PWD/qmljs.qrc
......
......@@ -369,7 +369,7 @@ bool Bind::visit(VariableDeclaration *ast)
if (! ast->name)
return false;
ASTVariableReference *ref = new ASTVariableReference(ast, &_valueOwner);
ASTVariableReference *ref = new ASTVariableReference(ast, _doc, &_valueOwner);
if (_currentObjectValue)
_currentObjectValue->setMember(ast->name->asString(), ref);
return true;
......
......@@ -365,17 +365,17 @@ private:
} // end of anonymous namespace
Check::Check(Document::Ptr doc, const Context *linkedContextNoScope)
Check::Check(Document::Ptr doc, const Context *context)
: _doc(doc)
, _context(*linkedContextNoScope)
, _scopeBuilder(&_context, doc)
, _context(*context)
, _scopeChain(doc, &_context)
, _scopeBuilder(&_scopeChain)
, _options(WarnDangerousNonStrictEqualityChecks | WarnBlocks | WarnWith
| WarnVoid | WarnCommaExpression | WarnExpressionStatement
| WarnAssignInCondition | WarnUseBeforeDeclaration | WarnDuplicateDeclaration
| WarnCaseWithoutFlowControlEnd | ErrCheckTypeErrors)
, _lastValue(0)
{
_scopeBuilder.initializeRootScope();
}
Check::~Check()
......@@ -509,8 +509,7 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
// suppress subsequent errors about scope object lookup by clearing
// the scope object list
// ### todo: better way?
_context.scopeChain().qmlScopeObjects.clear();
_context.scopeChain().update();
_scopeChain.setQmlScopeObjects(QList<const ObjectValue *>());
}
Node::accept(initializer, this);
......@@ -565,7 +564,7 @@ bool Check::visit(UiScriptBinding *ast)
if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) {
ExpressionNode *expr = expStmt->expression;
Evaluate evaluator(&_context);
Evaluate evaluator(&_scopeChain);
const Value *rhsValue = evaluator(expr);
const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
......@@ -606,7 +605,7 @@ bool Check::visit(IdentifierExpression *ast)
_lastValue = 0;
if (ast->name) {
Evaluate evaluator(&_context);
Evaluate evaluator(&_scopeChain);
_lastValue = evaluator.reference(ast);
if (!_lastValue)
error(ast->identifierToken, tr("unknown identifier"));
......@@ -683,7 +682,7 @@ bool Check::visit(BinaryExpression *ast)
if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) {
bool warn = _options & WarnAllNonStrictEqualityChecks;
if (!warn && _options & WarnDangerousNonStrictEqualityChecks) {
Evaluate eval(&_context);
Evaluate eval(&_scopeChain);
const Value *lhs = eval(ast->left);
const Value *rhs = eval(ast->right);
warn = shouldAvoidNonStrictEqualityCheck(ast->left, rhs)
......@@ -850,7 +849,7 @@ bool Check::visit(DefaultClause *ast)
/// ### Maybe put this into the context as a helper method.
const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{
QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects;
QList<const ObjectValue *> scopeObjects = _scopeChain.qmlScopeObjects();
if (scopeObjects.isEmpty())
return 0;
......@@ -869,7 +868,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
bool isAttachedProperty = false;
if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
isAttachedProperty = true;
if (const ObjectValue *qmlTypes = _context.scopeChain().qmlTypes)
if (const ObjectValue *qmlTypes = _scopeChain.qmlTypes())
scopeObjects += qmlTypes;
}
......
......@@ -36,6 +36,7 @@
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsscopebuilder.h>
#include <qmljs/qmljsscopechain.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <QtCore/QCoreApplication>
......@@ -52,7 +53,8 @@ class QMLJS_EXPORT Check: protected AST::Visitor
typedef QSet<QString> StringSet;
public:
Check(Document::Ptr doc, const Interpreter::Context *linkedContextNoScope);
// prefer taking root scope chain?
Check(Document::Ptr doc, const Interpreter::Context *context);
virtual ~Check();
QList<DiagnosticMessage> operator()();
......@@ -127,6 +129,7 @@ private:
Document::Ptr _doc;
Interpreter::Context _context;
Interpreter::ScopeChain _scopeChain;
ScopeBuilder _scopeBuilder;
QList<DiagnosticMessage> _messages;
......
......@@ -41,9 +41,7 @@ using namespace QmlJS::Interpreter;
Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports)
: _snapshot(snapshot),
_valueOwner(valueOwner),
_imports(imports),
_qmlScopeObjectIndex(-1),
_qmlScopeObjectSet(false)
_imports(imports)
{
}
......@@ -62,16 +60,6 @@ QmlJS::Snapshot Context::snapshot() const
return _snapshot;
}
const ScopeChain &Context::scopeChain() const
{
return _scopeChain;
}
ScopeChain &Context::scopeChain()
{
return _scopeChain;
}
const Imports *Context::imports(const QmlJS::Document *doc) const
{
if (!doc)
......@@ -79,24 +67,6 @@ const Imports *Context::imports(const QmlJS::Document *doc) const
return _imports.value(doc).data();
}
const Value *Context::lookup(const QString &name, const ObjectValue **foundInScope) const
{
QList<const ObjectValue *> scopes = _scopeChain.all();
for (int index = scopes.size() - 1; index != -1; --index) {
const ObjectValue *scope = scopes.at(index);
if (const Value *member = scope->lookupMember(name, this)) {
if (foundInScope)
*foundInScope = scope;
return member;
}
}
if (foundInScope)
*foundInScope = 0;
return _valueOwner->undefinedValue();
}
const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId *qmlTypeName,
UiQualifiedId *qmlTypeNameEnd) const
{
......@@ -147,18 +117,8 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QString
const Value *Context::lookupReference(const Value *value) const
{
const Reference *reference = value_cast<const Reference *>(value);
if (!reference)
return value;
if (_referenceStack.contains(reference))
return 0;
_referenceStack.append(reference);
const Value *v = reference->value(this);
_referenceStack.removeLast();
return v;
ReferenceContext refContext(this);
return refContext.lookupReference(value);
}
QString Context::defaultPropertyName(const ObjectValue *object) const
......@@ -176,3 +136,33 @@ QString Context::defaultPropertyName(const ObjectValue *object) const
}
return QString();
}
ReferenceContext::ReferenceContext(const Context *context)
: m_context(context)
{}
const Value *ReferenceContext::lookupReference(const Value *value)
{
const Reference *reference = value_cast<const Reference *>(value);
if (!reference)
return value;
if (m_references.contains(reference))
return reference; // ### error
m_references.append(reference);
const Value *v = reference->value(this);
m_references.removeLast();
return v;
}
const Context *ReferenceContext::context() const
{
return m_context;
}
ReferenceContext::operator const Context *() const
{
return m_context;
}
......@@ -45,9 +45,12 @@ class Snapshot;
namespace Interpreter {
// shared among threads, completely threadsafe
// currently also safe to copy, but will be deprecated
class QMLJS_EXPORT Context
{
public:
typedef QSharedPointer<Context> Ptr;
typedef QHash<const Document *, QSharedPointer<const Imports> > ImportsPerDocument;
// Context takes ownership of valueOwner
......@@ -57,12 +60,8 @@ public:
ValueOwner *valueOwner() const;
Snapshot snapshot() const;
const ScopeChain &scopeChain() const;
ScopeChain &scopeChain();
const Imports *imports(const Document *doc) const;
const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const;
const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName,
AST::UiQualifiedId *qmlTypeNameEnd = 0) const;
const ObjectValue *lookupType(const Document *doc, const QStringList &qmlTypeName) const;
......@@ -71,19 +70,29 @@ public:
QString defaultPropertyName(const ObjectValue *object) const;
private:
typedef QHash<QString, const Value *> Properties;
Snapshot _snapshot;
QSharedPointer<ValueOwner> _valueOwner;
ImportsPerDocument _imports;
ScopeChain _scopeChain;
int _qmlScopeObjectIndex;
bool _qmlScopeObjectSet;
};
// for looking up references
class QMLJS_EXPORT ReferenceContext
{
public:
// implicit conversion ok
ReferenceContext(const Context *context);
const Value *lookupReference(const Value *value);
const Context *context() const;
operator const Context *() const;
// for avoiding reference cycles during lookup
mutable QList<const Reference *> _referenceStack;
private:
const Context *m_context;
QList<const Reference *> m_references;
};
} // namespace Interpreter
} // namespace QmlJS
......
......@@ -33,15 +33,17 @@
#include "qmljsevaluate.h"
#include "qmljscontext.h"
#include "qmljsvalueowner.h"
#include "qmljsscopechain.h"
#include "parser/qmljsast_p.h"
#include <QtCore/QDebug>
using namespace QmlJS;
using namespace QmlJS::Interpreter;
Evaluate::Evaluate(const Context *context)
: _valueOwner(context->valueOwner()),
_context(context),
Evaluate::Evaluate(const ScopeChain *scopeChain)
: _valueOwner(scopeChain->context()->valueOwner()),
_context(scopeChain->context()),
_scopeChain(scopeChain),
_scope(_valueOwner->globalObject()),
_result(0)
{
......@@ -165,7 +167,7 @@ bool Evaluate::visit(AST::UiQualifiedId *ast)
if (! ast->name)
return false;
const Value *value = _context->lookup(ast->name->asString());
const Value *value = _scopeChain->lookup(ast->name->asString());
if (! ast->next) {
_result = value;
......@@ -213,7 +215,7 @@ bool Evaluate::visit(AST::IdentifierExpression *ast)
if (! ast->name)
return false;
_result = _context->lookup(ast->name->asString());
_result = _scopeChain->lookup(ast->name->asString());
return false;
}
......
......@@ -35,6 +35,7 @@
#include "parser/qmljsastvisitor_p.h"
#include "qmljsdocument.h"
#include "qmljsscopechain.h"
namespace QmlJS {
......@@ -49,7 +50,7 @@ namespace Interpreter {
class QMLJS_EXPORT Evaluate: protected AST::Visitor
{
public:
Evaluate(const Interpreter::Context *context);
Evaluate(const Interpreter::ScopeChain *scopeChain);
virtual ~Evaluate();
// same as value()
......@@ -165,6 +166,7 @@ private:
QmlJS::Document::Ptr _doc;
Interpreter::ValueOwner *_valueOwner;
const Interpreter::Context *_context;
const Interpreter::ScopeChain *_scopeChain;
const Interpreter::ObjectValue *_scope;
const Interpreter::Value *_result;
};
......
......@@ -35,8 +35,9 @@
#include "qmljslink.h"
#include "qmljsbind.h"
#include "qmljsscopebuilder.h"
#include "qmljstypedescriptionreader.h"
#include "qmljsscopechain.h"
#include "qmljsscopeastpath.h"
#include "qmljstypedescriptionreader.h"
#include "qmljsvalueowner.h"
#include "qmljscontext.h"
#include "parser/qmljsast_p.h"
......@@ -688,82 +689,6 @@ void StringValue::accept(ValueVisitor *visitor) const
visitor->visit(this);
}
ScopeChain::ScopeChain()
: globalScope(0)
, qmlTypes(0)
, jsImports(0)
{
}
ScopeChain::QmlComponentChain::QmlComponentChain()
{
}
ScopeChain::QmlComponentChain::~QmlComponentChain()
{
qDeleteAll(instantiatingComponents);
}
void ScopeChain::QmlComponentChain::clear()
{
qDeleteAll(instantiatingComponents);
instantiatingComponents.clear();
document = Document::Ptr(0);
}
void ScopeChain::QmlComponentChain::collect(QList<const ObjectValue *> *list) const
{
foreach (const QmlComponentChain *parent, instantiatingComponents)
parent->collect(list);
if (!document)
return;
if (ObjectValue *root = document->bind()->rootObjectValue())
list->append(root);
if (ObjectValue *ids = document->bind()->idEnvironment())
list->append(ids);
}
void ScopeChain::update()
{
_all.clear();
_all += globalScope;
// the root scope in js files doesn't see instantiating components
if (jsScopes.count() != 1 || !qmlScopeObjects.isEmpty()) {
if (qmlComponentScope) {
foreach (const QmlComponentChain *parent, qmlComponentScope->instantiatingComponents)
parent->collect(&_all);
}
}
ObjectValue *root = 0;
ObjectValue *ids = 0;
if (qmlComponentScope && qmlComponentScope->document) {
root = qmlComponentScope->document->bind()->rootObjectValue();
ids = qmlComponentScope->document->bind()->idEnvironment();
}
if (root && !qmlScopeObjects.contains(root))
_all += root;
_all += qmlScopeObjects;
if (ids)
_all += ids;
if (qmlTypes)
_all += qmlTypes;
if (jsImports)
_all += jsImports;
_all += jsScopes;
}
QList<const ObjectValue *> ScopeChain::all() const
{
return _all;
}
Reference::Reference(ValueOwner *valueOwner)
: _valueOwner(valueOwner)
{
......@@ -789,7 +714,7 @@ void Reference::accept(ValueVisitor *visitor) const
visitor->visit(this);
}
const Value *Reference::value(const Context *) const
const Value *Reference::value(const ReferenceContext *) const
{
return _valueOwner->undefinedValue();
}
......@@ -1771,8 +1696,10 @@ const QmlJS::Document *ASTObjectValue::document() const
return _doc;
}
ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, ValueOwner *valueOwner)
: Reference(valueOwner), _ast(ast)
ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, const QmlJS::Document *doc, ValueOwner *valueOwner)
: Reference(valueOwner)
, _ast(ast)
, _doc(doc)
{
}
......@@ -1780,10 +1707,18 @@ ASTVariableReference::~ASTVariableReference()
{
}
const Value *ASTVariableReference::value(const Context *context) const
const Value *ASTVariableReference::value(const ReferenceContext *referenceContext) const
{
Evaluate check(context);
return check(_ast->expression);
if (!_ast->expression)
return valueOwner()->undefinedValue();
Document::Ptr doc = _doc->ptr();
ScopeChain scopeChain(doc, referenceContext->context());
QmlJS::ScopeBuilder builder(&scopeChain);
builder.push(QmlJS::ScopeAstPath(doc)(_ast->expression->firstSourceLocation().begin()));
QmlJS::Evaluate evaluator(&scopeChain);
return evaluator(_ast->expression);
}
ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const QmlJS::Document *doc, ValueOwner *valueOwner)
......@@ -1859,9 +1794,9 @@ UiQualifiedId *QmlPrototypeReference::qmlTypeName() const
return _qmlTypeName;
}
const Value *QmlPrototypeReference::value(const Context *context) const
const Value *QmlPrototypeReference::value(const ReferenceContext *referenceContext) const
{
return context->lookupType(_doc, _qmlTypeName);
return referenceContext->context()->lookupType(_doc, _qmlTypeName);
}
ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const QmlJS::Document *doc, ValueOwner *valueOwner)
......@@ -1886,7 +1821,7 @@ bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int *
return true;
}
const Value *ASTPropertyReference::value(const Context *context) const
const Value *ASTPropertyReference::value(const ReferenceContext *referenceContext) const
{
if (_ast->statement
&& (!_ast->memberType || _ast->memberType->asString() == QLatin1String("variant")
......@@ -1896,15 +1831,14 @@ const Value *ASTPropertyReference::value(const Context *context) const
// ### Improve efficiency by caching the 'use chain' constructed in ScopeBuilder.
QmlJS::Document::Ptr doc = _doc->ptr();
Context localContext(*context);
QmlJS::ScopeBuilder builder(&localContext, doc);
builder.initializeRootScope();
ScopeChain scopeChain(doc, referenceContext->context());
QmlJS::ScopeBuilder builder(&scopeChain);
int offset = _ast->statement->firstSourceLocation().begin();
builder.push(ScopeAstPath(doc)(offset));
Evaluate check(&localContext);
return check(_ast->statement);
QmlJS::Evaluate evaluator(&scopeChain);
return evaluator(_ast->statement);
}
if (_ast->memberType)
......@@ -1934,7 +1868,7 @@ bool ASTSignalReference::getSourceLocation(QString *fileName, int *line, int *co
return true;
}
const Value *ASTSignalReference::value(const Context *) const
const Value *ASTSignalReference::value(const ReferenceContext *) const
{
return valueOwner()->undefinedValue();
}
......
......@@ -75,6 +75,7 @@ class Imports;
class TypeScope;
class JSImportScope;
class Context;
class ReferenceContext;
typedef QList<const Value *> ValueList;
......@@ -283,40 +284,6 @@ public:
virtual bool processGeneratedSlot(const QString &name, const Value *value);
};
class QMLJS_EXPORT ScopeChain
{
public:
ScopeChain();
class QMLJS_EXPORT QmlComponentChain
{
Q_DISABLE_COPY(QmlComponentChain)
public:
QmlComponentChain();
~QmlComponentChain();
QList<const QmlComponentChain *> instantiatingComponents;
Document::Ptr document;
void collect(QList<const ObjectValue *> *list) const;
void clear();
};
const ObjectValue *globalScope;
QSharedPointer<const QmlComponentChain> qmlComponentScope;
QList<const ObjectValue *> qmlScopeObjects;
const TypeScope *qmlTypes;
const JSImportScope *jsImports;
QList<const ObjectValue *> jsScopes;
// rebuilds the flat list of all scopes
void update();
QList<const ObjectValue *> all() const;
private:
QList<const ObjectValue *> _all;
};
class QMLJS_EXPORT Reference: public Value
{
public:
......@@ -330,10 +297,10 @@ public:
virtual void accept(ValueVisitor *) const;
private:
virtual const Value *value(const Context *context) const;
virtual const Value *value(const ReferenceContext *referenceContext) const;
ValueOwner *_valueOwner;
friend class Context;
friend class ReferenceContext;
};
class QMLJS_EXPORT ColorValue: public Value
......@@ -724,7 +691,7 @@ public:
AST::UiQualifiedId *qmlTypeName() const;
private: