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/1694
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
Christian Kamm
2011-07-12 14:55:27 +02:00
parent ed1321a4f9
commit f87dc61986
22 changed files with 671 additions and 416 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 avoiding reference cycles during lookup
mutable QList<const Reference *> _referenceStack;
};
// 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;
private:
const Context *m_context;
QList<const Reference *> m_references;
};
} // namespace Interpreter
} // namespace QmlJS

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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();
}

View File

@@ -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:
virtual const Value *value(const Context *context) const;
virtual const Value *value(const ReferenceContext *referenceContext) const;
AST::UiQualifiedId *_qmlTypeName;
const Document *_doc;
@@ -733,13 +700,14 @@ private:
class QMLJS_EXPORT ASTVariableReference: public Reference
{
AST::VariableDeclaration *_ast;
const Document *_doc;
public:
ASTVariableReference(AST::VariableDeclaration *ast, ValueOwner *valueOwner);
ASTVariableReference(AST::VariableDeclaration *ast, const Document *doc, ValueOwner *valueOwner);
virtual ~ASTVariableReference();
private:
virtual const Value *value(const Context *context) const;
virtual const Value *value(const ReferenceContext *referenceContext) const;
};
class QMLJS_EXPORT ASTFunctionValue: public FunctionValue
@@ -779,7 +747,7 @@ public:
virtual bool getSourceLocation(QString *fileName, int *line, int *column) const;
private:
virtual const Value *value(const Context *context) const;
virtual const Value *value(const ReferenceContext *referenceContext) const;
};
class QMLJS_EXPORT ASTSignalReference: public Reference
@@ -798,7 +766,7 @@ public:
virtual bool getSourceLocation(QString *fileName, int *line, int *column) const;
private:
virtual const Value *value(const Context *context) const;
virtual const Value *value(const ReferenceContext *referenceContext) const;
};
class QMLJS_EXPORT ASTObjectValue: public ObjectValue

View File

@@ -36,6 +36,7 @@
#include "qmljsscopebuilder.h"
#include "qmljsmodelmanagerinterface.h"
#include "qmljsevaluate.h"
#include "qmljsscopechain.h"
using namespace QmlJS;
@@ -47,26 +48,26 @@ public:
, context(Link(snapshot,
ModelManagerInterface::instance()->importPaths(),
ModelManagerInterface::instance()->builtins(doc))())
, scopeChain(doc, &context)
{
ScopeBuilder scopeBuilder(&context, doc);
scopeBuilder.initializeRootScope();
ScopeBuilder scopeBuilder(&scopeChain);
scopeBuilder.push(path);
}
LookupContextData(Document::Ptr doc,
const Interpreter::Context &contextWithoutScope,
const Interpreter::Context &context,
const QList<AST::Node *> &path)
: doc(doc)
, context(contextWithoutScope)
, context(context)
, scopeChain(doc, &context)
{
ScopeBuilder scopeBuilder(&context, doc);
scopeBuilder.initializeRootScope();
ScopeBuilder scopeBuilder(&scopeChain);
scopeBuilder.push(path);
}
Document::Ptr doc;
Interpreter::Context context;
// snapshot is in context
Interpreter::ScopeChain scopeChain;
};
LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path)
@@ -75,9 +76,9 @@ LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const
}
LookupContext::LookupContext(const Document::Ptr doc,
const Interpreter::Context &contextWithoutScope,
const Interpreter::Context &context,
const QList<AST::Node *> &path)
: d(new LookupContextData(doc, contextWithoutScope, path))
: d(new LookupContextData(doc, context, path))
{
}
@@ -92,16 +93,16 @@ LookupContext::Ptr LookupContext::create(Document::Ptr doc, const Snapshot &snap
}
LookupContext::Ptr LookupContext::create(const Document::Ptr doc,
const Interpreter::Context &linkedContextWithoutScope,
const Interpreter::Context &context,
const QList<AST::Node *> &path)
{
Ptr ptr(new LookupContext(doc, linkedContextWithoutScope, path));
Ptr ptr(new LookupContext(doc, context, path));
return ptr;
}
const Interpreter::Value *LookupContext::evaluate(AST::Node *node) const
{
Evaluate check(&d->context);
Evaluate check(&d->scopeChain);
return check(node);
}
@@ -126,3 +127,8 @@ const Interpreter::Context *LookupContext::context() const
{
return &d->context;
}
const Interpreter::ScopeChain &LookupContext::scopeChain() const
{
return d->scopeChain;
}

View File

@@ -47,6 +47,7 @@ class LookupContextData;
namespace Interpreter {
class Value;
class Context;
class ScopeChain;
}
class QMLJS_EXPORT LookupContext
@@ -55,7 +56,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 &linkedContextWithoutScope,
const Interpreter::Context &context,
const QList<AST::Node *> &path);
public:
@@ -67,7 +68,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 &linkedContextWithoutScope,
const Interpreter::Context &context,
const QList<AST::Node *> &path);
const Interpreter::Value *evaluate(AST::Node *node) const;
@@ -76,6 +77,7 @@ public:
Snapshot snapshot() const;
Interpreter::ValueOwner *valueOwner() const;
const Interpreter::Context *context() const;
const Interpreter::ScopeChain &scopeChain() const;
private:
QScopedPointer<LookupContextData> d;

View File

@@ -35,15 +35,15 @@
#include "qmljsbind.h"
#include "qmljscontext.h"
#include "qmljsevaluate.h"
#include "qmljsscopechain.h"
#include "parser/qmljsast_p.h"
using namespace QmlJS;
using namespace QmlJS::Interpreter;
using namespace QmlJS::AST;
ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc)
: _doc(doc)
, _context(context)
ScopeBuilder::ScopeBuilder(ScopeChain *scopeChain)
: _scopeChain(scopeChain)
{
}
@@ -69,16 +69,17 @@ void ScopeBuilder::push(AST::Node *node)
case Node::Kind_FunctionExpression:
case Node::Kind_UiPublicMember:
{
ObjectValue *scope = _doc->bind()->findAttachedJSScope(node);
if (scope)
_context->scopeChain().jsScopes += scope;
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node);
if (scope) {
QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes();
jsScopes += scope;
_scopeChain->setJsScopes(jsScopes);
}
break;
}
default:
break;
}
_context->scopeChain().update();
}
void ScopeBuilder::push(const QList<AST::Node *> &nodes)
@@ -99,9 +100,14 @@ void ScopeBuilder::pop()
case Node::Kind_FunctionExpression:
case Node::Kind_UiPublicMember:
{
ObjectValue *scope = _doc->bind()->findAttachedJSScope(toRemove);
if (scope)
_context->scopeChain().jsScopes.removeLast();
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(toRemove);
if (scope) {
QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes();
if (!jsScopes.isEmpty()) {
jsScopes.removeLast();
_scopeChain->setJsScopes(jsScopes);
}
}
break;
}
default:
@@ -112,103 +118,12 @@ void ScopeBuilder::pop()
if (! _nodes.isEmpty()
&& (cast<UiObjectDefinition *>(toRemove) || cast<UiObjectBinding *>(toRemove)))
setQmlScopeObject(_nodes.last());
_context->scopeChain().update();
}
void ScopeBuilder::initializeRootScope()
{
ScopeChain &scopeChain = _context->scopeChain();
if (scopeChain.qmlComponentScope
&& scopeChain.qmlComponentScope->document == _doc) {
return;
}
scopeChain = ScopeChain(); // reset
Interpreter::ValueOwner *valueOwner = _context->valueOwner();
// ### TODO: This object ought to contain the global namespace additions by QML.
scopeChain.globalScope = valueOwner->globalObject();
if (! _doc) {
scopeChain.update();
return;
}
Bind *bind = _doc->bind();
QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes;
const Snapshot &snapshot = _context->snapshot();
ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain;
scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain);
if (_doc->qmlProgram()) {
componentScopes.insert(_doc.data(), chain);
makeComponentChain(_doc, snapshot, chain, &componentScopes);
if (const Imports *imports = _context->imports(_doc.data())) {
scopeChain.qmlTypes = imports->typeScope();
scopeChain.jsImports = imports->jsImportScope();
}
} else {
// add scope chains for all components that import this file
foreach (Document::Ptr otherDoc, snapshot) {
foreach (const ImportInfo &import, otherDoc->bind()->imports()) {
if (import.type() == ImportInfo::FileImport && _doc->fileName() == import.name()) {
ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
componentScopes.insert(otherDoc.data(), component);
chain->instantiatingComponents += component;
makeComponentChain(otherDoc, snapshot, component, &componentScopes);
}
}
}
// ### TODO: Which type environment do scripts see?
if (bind->rootObjectValue())
scopeChain.jsScopes += bind->rootObjectValue();
}
scopeChain.update();
}
void ScopeBuilder::makeComponentChain(
Document::Ptr doc,
const Snapshot &snapshot,
ScopeChain::QmlComponentChain *target,
QHash<Document *, ScopeChain::QmlComponentChain *> *components)
{
if (!doc->qmlProgram())
return;
Bind *bind = doc->bind();
// add scopes for all components instantiating this one
foreach (Document::Ptr otherDoc, snapshot) {
if (otherDoc == doc)
continue;
if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) {
if (components->contains(otherDoc.data())) {
// target->instantiatingComponents += components->value(otherDoc.data());
} else {
ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
components->insert(otherDoc.data(), component);
target->instantiatingComponents += component;
makeComponentChain(otherDoc, snapshot, component, components);
}
}
}
// build this component scope
target->document = doc;
}
void ScopeBuilder::setQmlScopeObject(Node *node)
{
ScopeChain &scopeChain = _context->scopeChain();
if (_doc->bind()->isGroupedPropertyBinding(node)) {
QList<const ObjectValue *> qmlScopeObjects;
if (_scopeChain->document()->bind()->isGroupedPropertyBinding(node)) {
UiObjectDefinition *definition = cast<UiObjectDefinition *>(node);
if (!definition)
return;
@@ -219,21 +134,21 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
if (!object)
return;
scopeChain.qmlScopeObjects.clear();
scopeChain.qmlScopeObjects += object;
qmlScopeObjects += object;
_scopeChain->setQmlScopeObjects(qmlScopeObjects);
return;
}
const ObjectValue *scopeObject = _doc->bind()->findQmlObject(node);
const ObjectValue *scopeObject = _scopeChain->document()->bind()->findQmlObject(node);
if (scopeObject) {
scopeChain.qmlScopeObjects.clear();
scopeChain.qmlScopeObjects += scopeObject;
qmlScopeObjects += scopeObject;
} else {
return; // Probably syntax errors, where we're working with a "recovered" AST.
}
// check if the object has a Qt.ListElement or Qt.Connections ancestor
// ### allow only signal bindings for Connections
PrototypeIterator iter(scopeObject, _context);
PrototypeIterator iter(scopeObject, _scopeChain->context());
iter.next();
while (iter.hasNext()) {
const ObjectValue *prototype = iter.next();
@@ -242,15 +157,15 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
|| qmlMetaObject->className() == QLatin1String("Connections")
) && (qmlMetaObject->packageName() == QLatin1String("Qt")
|| qmlMetaObject->packageName() == QLatin1String("QtQuick"))) {
scopeChain.qmlScopeObjects.clear();
qmlScopeObjects.clear();
break;
}
}
}
// check if the object has a Qt.PropertyChanges ancestor
const ObjectValue *prototype = scopeObject->prototype(_context);
prototype = isPropertyChangesObject(_context, prototype);
const ObjectValue *prototype = scopeObject->prototype(_scopeChain->context());
prototype = isPropertyChangesObject(_scopeChain->context(), prototype);
// find the target script binding
if (prototype) {
UiObjectInitializer *initializer = 0;
@@ -264,31 +179,33 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name
&& scriptBinding->qualifiedId->name->asString() == QLatin1String("target")
&& ! scriptBinding->qualifiedId->next) {
Evaluate evaluator(_context);
Evaluate evaluator(_scopeChain);
const Value *targetValue = evaluator(scriptBinding->statement);
if (const ObjectValue *target = value_cast<const ObjectValue *>(targetValue)) {
scopeChain.qmlScopeObjects.prepend(target);
qmlScopeObjects.prepend(target);
} else {
scopeChain.qmlScopeObjects.clear();
qmlScopeObjects.clear();
}
}
}
}
}
}
_scopeChain->setQmlScopeObjects(qmlScopeObjects);
}
const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id)
{
// do a name lookup on the scope objects
const Value *result = 0;
foreach (const ObjectValue *scopeObject, _context->scopeChain().qmlScopeObjects) {
foreach (const ObjectValue *scopeObject, _scopeChain->qmlScopeObjects()) {
const ObjectValue *object = scopeObject;
for (UiQualifiedId *it = id; it; it = it->next) {
if (!it->name)
return 0;
result = object->lookupMember(it->name->asString(), _context);
result = object->lookupMember(it->name->asString(), _scopeChain->context());
if (!result)
break;
if (it->next) {

View File

@@ -34,12 +34,19 @@
#define QMLJSSCOPEBUILDER_H
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsinterpreter.h>
#include <QtCore/QList>
namespace QmlJS {
namespace Interpreter {
class QmlComponentChain;
class Context;
class ObjectValue;
class Value;
class ScopeChain;
}
namespace AST {
class Node;
}
@@ -47,11 +54,9 @@ namespace AST {
class QMLJS_EXPORT ScopeBuilder
{
public:
ScopeBuilder(Interpreter::Context *context, Document::Ptr doc);
ScopeBuilder(Interpreter::ScopeChain *scopeChain);
~ScopeBuilder();
void initializeRootScope();
void push(AST::Node *node);
void push(const QList<AST::Node *> &nodes);
void pop();
@@ -59,15 +64,10 @@ public:
static const Interpreter::ObjectValue *isPropertyChangesObject(const Interpreter::Context *context, const Interpreter::ObjectValue *object);
private:
void makeComponentChain(Document::Ptr doc, const Snapshot &snapshot,
Interpreter::ScopeChain::QmlComponentChain *target,
QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components);
void setQmlScopeObject(AST::Node *node);
const Interpreter::Value *scopeObjectLookup(AST::UiQualifiedId *id);
Document::Ptr _doc;
Interpreter::Context *_context;
Interpreter::ScopeChain *_scopeChain;
QList<AST::Node *> _nodes;
};

View File

@@ -0,0 +1,291 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljsscopechain.h"
#include "qmljsbind.h"
using namespace QmlJS;
using namespace QmlJS::Interpreter;
QmlComponentChain::QmlComponentChain(const Document::Ptr &document)
: m_document(document)
{
}
QmlComponentChain::~QmlComponentChain()
{
qDeleteAll(m_instantiatingComponents);
}
Document::Ptr QmlComponentChain::document() const
{
return m_document;
}
QList<const QmlComponentChain *> QmlComponentChain::instantiatingComponents() const
{
return m_instantiatingComponents;
}
void QmlComponentChain::addInstantiatingComponent(const QmlComponentChain *component)
{
m_instantiatingComponents.append(component);
}
ScopeChain::ScopeChain(const Document::Ptr &document, const Context *context)
: m_document(document)
, m_context(context)
, m_globalScope(0)
, m_qmlTypes(0)
, m_jsImports(0)
, m_modified(false)
{
initializeRootScope();
}
Document::Ptr ScopeChain::document() const
{
return m_document;
}
const Context *ScopeChain::context() const
{
return m_context;
}
const Value * ScopeChain::lookup(const QString &name, const ObjectValue **foundInScope) const
{
QList<const ObjectValue *> scopes = all();
for (int index = scopes.size() - 1; index != -1; --index) {
const ObjectValue *scope = scopes.at(index);
if (const Value *member = scope->lookupMember(name, m_context)) {
if (foundInScope)
*foundInScope = scope;
return member;
}
}
if (foundInScope)
*foundInScope = 0;
return m_context->valueOwner()->undefinedValue();
}
const ObjectValue *ScopeChain::globalScope() const
{
return m_globalScope;
}
void ScopeChain::setGlobalScope(const ObjectValue *globalScope)
{
m_modified = true;
m_globalScope = globalScope;
}
QSharedPointer<const QmlComponentChain> ScopeChain::qmlComponentChain() const
{
return m_qmlComponentScope;
}
void ScopeChain::setQmlComponentChain(const QSharedPointer<const QmlComponentChain> &qmlComponentChain)
{
m_modified = true;
m_qmlComponentScope = qmlComponentChain;
}
QList<const ObjectValue *> ScopeChain::qmlScopeObjects() const
{
return m_qmlScopeObjects;
}
void ScopeChain::setQmlScopeObjects(const QList<const ObjectValue *> &qmlScopeObjects)
{
m_modified = true;
m_qmlScopeObjects = qmlScopeObjects;
}
const TypeScope *ScopeChain::qmlTypes() const
{
return m_qmlTypes;
}
void ScopeChain::setQmlTypes(const TypeScope *qmlTypes)
{
m_modified = true;
m_qmlTypes = qmlTypes;
}
const JSImportScope *ScopeChain::jsImports() const
{
return m_jsImports;
}
void ScopeChain::setJsImports(const JSImportScope *jsImports)
{
m_modified = true;
m_jsImports = jsImports;
}
QList<const ObjectValue *> ScopeChain::jsScopes() const
{
return m_jsScopes;
}
void ScopeChain::setJsScopes(const QList<const ObjectValue *> &jsScopes)
{
m_modified = true;
m_jsScopes = jsScopes;
}
QList<const ObjectValue *> ScopeChain::all() const
{
if (m_modified)
update();
return m_all;
}
static void collectScopes(const QmlComponentChain *chain, QList<const ObjectValue *> *target)
{
foreach (const QmlComponentChain *parent, chain->instantiatingComponents())
collectScopes(parent, target);
if (!chain->document())
return;
if (ObjectValue *root = chain->document()->bind()->rootObjectValue())
target->append(root);
if (ObjectValue *ids = chain->document()->bind()->idEnvironment())
target->append(ids);
}
void ScopeChain::update() const
{
m_all.clear();
m_all += m_globalScope;
// the root scope in js files doesn't see instantiating components
if (m_jsScopes.count() != 1 || !m_qmlScopeObjects.isEmpty()) {
if (m_qmlComponentScope) {
foreach (const QmlComponentChain *parent, m_qmlComponentScope->instantiatingComponents())
collectScopes(parent, &m_all);
}
}
ObjectValue *root = 0;
ObjectValue *ids = 0;
if (m_qmlComponentScope && m_qmlComponentScope->document()) {
const Bind *bind = m_qmlComponentScope->document()->bind();
root = bind->rootObjectValue();
ids = bind->idEnvironment();
}
if (root && !m_qmlScopeObjects.contains(root))
m_all += root;
m_all += m_qmlScopeObjects;
if (ids)
m_all += ids;
if (m_qmlTypes)
m_all += m_qmlTypes;
if (m_jsImports)
m_all += m_jsImports;
m_all += m_jsScopes;
}
void ScopeChain::initializeRootScope()
{
ValueOwner *valueOwner = m_context->valueOwner();
const Snapshot &snapshot = m_context->snapshot();
Bind *bind = m_document->bind();
m_globalScope = valueOwner->globalObject();
QHash<Document *, QmlComponentChain *> componentScopes;
QmlComponentChain *chain = new QmlComponentChain(m_document);
m_qmlComponentScope = QSharedPointer<const QmlComponentChain>(chain);
if (m_document->qmlProgram()) {
componentScopes.insert(m_document.data(), chain);
makeComponentChain(chain, snapshot, &componentScopes);
if (const Imports *imports = m_context->imports(m_document.data())) {
m_qmlTypes = imports->typeScope();
m_jsImports = imports->jsImportScope();
}
} else {
// add scope chains for all components that import this file
foreach (Document::Ptr otherDoc, snapshot) {
foreach (const ImportInfo &import, otherDoc->bind()->imports()) {
if (import.type() == ImportInfo::FileImport && m_document->fileName() == import.name()) {
QmlComponentChain *component = new QmlComponentChain(otherDoc);
componentScopes.insert(otherDoc.data(), component);
chain->addInstantiatingComponent(component);
makeComponentChain(component, snapshot, &componentScopes);
}
}
}
// ### TODO: Which type environment do scripts see?
if (bind->rootObjectValue())
m_jsScopes += bind->rootObjectValue();
}
}
void ScopeChain::makeComponentChain(
QmlComponentChain *target,
const Snapshot &snapshot,
QHash<Document *, QmlComponentChain *> *components)
{
Document::Ptr doc = target->document();
if (!doc->qmlProgram())
return;
const Bind *bind = doc->bind();
// add scopes for all components instantiating this one
foreach (Document::Ptr otherDoc, snapshot) {
if (otherDoc == doc)
continue;
if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), m_context)) {
if (!components->contains(otherDoc.data())) {
QmlComponentChain *component = new QmlComponentChain(otherDoc);
components->insert(otherDoc.data(), component);
target->addInstantiatingComponent(component);
makeComponentChain(component, snapshot, components);
}
}
}
}

View File

@@ -0,0 +1,127 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJS_SCOPECHAIN_H
#define QMLJS_SCOPECHAIN_H
#include "qmljs_global.h"
#include "qmljsdocument.h"
#include "qmljscontext.h"
#include <QtCore/QList>
#include <QtCore/QSharedPointer>
namespace QmlJS {
namespace Interpreter {
class ObjectValue;
class TypeScope;
class JSImportScope;
class Context;
class Value;
class QMLJS_EXPORT QmlComponentChain
{
Q_DISABLE_COPY(QmlComponentChain)
public:
QmlComponentChain(const Document::Ptr &document);
~QmlComponentChain();
Document::Ptr document() const;
QList<const QmlComponentChain *> instantiatingComponents() const;
// takes ownership
void addInstantiatingComponent(const QmlComponentChain *component);
private:
QList<const QmlComponentChain *> m_instantiatingComponents;
Document::Ptr m_document;
};
// scope chains are copyable
// constructing a new scope chain is currently too expensive:
// building the instantiating component chain needs to be sped up
class QMLJS_EXPORT ScopeChain
{
public:
explicit ScopeChain(const Document::Ptr &document, const Context *context);
Document::Ptr document() const;
const Context *context() const;
const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const;
const ObjectValue *globalScope() const;
void setGlobalScope(const ObjectValue *globalScope);
QSharedPointer<const QmlComponentChain> qmlComponentChain() const;
void setQmlComponentChain(const QSharedPointer<const QmlComponentChain> &qmlComponentChain);
QList<const ObjectValue *> qmlScopeObjects() const;
void setQmlScopeObjects(const QList<const ObjectValue *> &qmlScopeObjects);
const TypeScope *qmlTypes() const;
void setQmlTypes(const TypeScope *qmlTypes);
const JSImportScope *jsImports() const;
void setJsImports(const JSImportScope *jsImports);
QList<const ObjectValue *> jsScopes() const;
void setJsScopes(const QList<const ObjectValue *> &jsScopes);
QList<const ObjectValue *> all() const;
private:
void update() const;
void initializeRootScope();
void makeComponentChain(QmlComponentChain *target, const Snapshot &snapshot,
QHash<Document *, QmlComponentChain *> *components);
Document::Ptr m_document;
const Context *m_context;
const ObjectValue *m_globalScope;
QSharedPointer<const QmlComponentChain> m_qmlComponentScope;
QList<const ObjectValue *> m_qmlScopeObjects;
const TypeScope *m_qmlTypes;
const JSImportScope *m_jsImports;
QList<const ObjectValue *> m_jsScopes;
bool m_modified;
mutable QList<const ObjectValue *> m_all;
};
} // namespace Interpreter
} // namespace QmlJS
#endif // QMLJS_SCOPECHAIN_H