forked from qt-creator/qt-creator
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:
@@ -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 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
|
||||
|
||||
|
||||
@@ -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:
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
291
src/libs/qmljs/qmljsscopechain.cpp
Normal file
291
src/libs/qmljs/qmljsscopechain.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
127
src/libs/qmljs/qmljsscopechain.h
Normal file
127
src/libs/qmljs/qmljsscopechain.h
Normal 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
|
||||
Reference in New Issue
Block a user