Bind each QML document after parsing. Link them before use.

Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
Christian Kamm
2010-02-02 15:55:17 +01:00
parent e7a330f92a
commit 03fa188b41
8 changed files with 321 additions and 315 deletions

View File

@@ -121,76 +121,27 @@ public:
} }
}; };
class ProcessSourceElements: protected AST::Visitor
{
Interpreter::Engine *_interp;
public:
ProcessSourceElements(Interpreter::Engine *interp)
: _interp(interp),
typeOfExpression(interp)
{
}
void operator()(AST::Node *node)
{
if (node)
node->accept(this);
}
protected:
using AST::Visitor::visit;
virtual bool visit(FunctionDeclaration *ast)
{
if (ast->name)
_interp->globalObject()->setProperty(ast->name->asString(), new ASTFunctionValue(ast, _interp));
return false;
}
virtual bool visit(VariableDeclaration *ast)
{
if (ast->name) {
const Value *value = _interp->undefinedValue();
if (ast->expression)
value = typeOfExpression(ast->expression, _interp->globalObject());
_interp->globalObject()->setProperty(ast->name->asString(), value);
}
return false;
}
Check typeOfExpression;
};
} // end of anonymous namespace } // end of anonymous namespace
Bind::Bind(Document::Ptr doc, Interpreter::Engine *interp) Bind::Bind(Document *doc)
: _doc(doc), : _doc(doc),
_interp(interp),
_currentObjectValue(0), _currentObjectValue(0),
_typeEnvironment(0),
_idEnvironment(0), _idEnvironment(0),
_functionEnvironment(0), _functionEnvironment(0),
_rootObjectValue(0) _rootObjectValue(0)
{ {
if (_doc && _doc->qmlProgram() != 0) { if (!_doc)
UiProgram *program = _doc->qmlProgram(); return;
_currentObjectValue = 0; if (_doc->qmlProgram()) {
_typeEnvironment = _interp->newObject(/*prototype =*/ 0); _idEnvironment = _interp.newObject(/*prototype =*/ 0);
_idEnvironment = _interp->newObject(/*prototype =*/ 0); _functionEnvironment = _interp.newObject(/*prototype =*/ 0);
_functionEnvironment = _interp->newObject(/*prototype =*/ 0); } else if (_doc->jsProgram()) {
_rootObjectValue = 0; _currentObjectValue = _interp.globalObject();
_rootObjectValue = _interp.globalObject();
_qmlObjectDefinitions.clear();
_qmlObjectBindings.clear();
accept(program);
} }
accept(_doc->ast());
} }
Bind::~Bind() Bind::~Bind()
@@ -214,77 +165,6 @@ ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue)
return oldObjectValue; return oldObjectValue;
} }
ObjectValue *Bind::scopeChainAt(Document::Ptr currentDocument, const Snapshot &snapshot,
Interpreter::Engine *interp, AST::UiObjectMember *currentObject)
{
Bind *currentBind = 0;
QList<Bind *> binds;
QSet<QString> processed;
QStringList todo;
QMultiHash<QString, Document::Ptr> documentByPath;
foreach (Document::Ptr doc, snapshot)
documentByPath.insert(doc->path(), doc);
todo.append(currentDocument->path());
// Bind the reachable documents.
while (! todo.isEmpty()) {
const QString path = todo.takeFirst();
if (processed.contains(path))
continue;
processed.insert(path);
QStringList localImports;
foreach (Document::Ptr doc, documentByPath.values(path)) {
Bind *newBind = new Bind(doc, interp);
binds += newBind;
localImports += newBind->localImports();
if (doc == currentDocument)
currentBind = newBind;
}
localImports.removeDuplicates();
todo += localImports;
}
LinkImports linkImports;
Link link;
// link the import directives
linkImports(binds);
// link the scripts
QStringList includedScriptFiles;
foreach (Bind *bind, binds)
includedScriptFiles += bind->includedScripts();
includedScriptFiles.removeDuplicates();
ProcessSourceElements processSourceElements(interp);
foreach (const QString &scriptFile, includedScriptFiles) {
if (Document::Ptr scriptDoc = snapshot.document(scriptFile)) {
if (AST::Program *program = scriptDoc->jsProgram()) {
processSourceElements(program);
}
}
}
ObjectValue *scope = interp->globalObject();
if (currentBind)
scope = link(binds, currentBind, currentObject);
qDeleteAll(binds);
return scope;
}
QString Bind::toString(UiQualifiedId *qualifiedId, QChar delimiter) QString Bind::toString(UiQualifiedId *qualifiedId, QChar delimiter)
{ {
QString result; QString result;
@@ -350,7 +230,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
} }
// normal component instance // normal component instance
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _interp); ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp);
parentObjectValue = switchObjectValue(objectValue); parentObjectValue = switchObjectValue(objectValue);
if (parentObjectValue) if (parentObjectValue)
@@ -368,64 +248,9 @@ void Bind::accept(Node *node)
Node::accept(node, this); Node::accept(node, this);
} }
/*
import Qt 4.6
import Qt 4.6 as Xxx
(import com.nokia.qt is the same as the ones above)
import "content"
import "content" as Xxx
import "content" 4.6
import "content" 4.6 as Xxx
import "http://www.ovi.com/" as Ovi
*/
// ### TODO: Move to LinkImports
bool Bind::visit(UiImport *ast) bool Bind::visit(UiImport *ast)
{ {
if (! (ast->importUri || ast->fileName)) if (ast->fileName) {
return false; // nothing to do.
ObjectValue *namespaceObject = 0;
if (ast->asToken.isValid()) { // with namespace we insert an object in the type env. to hold the imported types
if (!ast->importId)
return false; // this should never happen, but better be safe than sorry
namespaceObject = _interp->newObject(/*prototype */ 0);
_typeEnvironment->setProperty(ast->importId->asString(), namespaceObject);
} else { // without namespace we insert all types directly into the type env.
namespaceObject = _typeEnvironment;
}
// look at files first
// else try the metaobject system
if (ast->importUri) {
const QString package = toString(ast->importUri, '/');
int majorVersion = -1; // ### TODO: Check these magic version numbers
int minorVersion = -1; // ### TODO: Check these magic version numbers
if (ast->versionToken.isValid()) {
const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length);
const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1) {
// only major (which is probably invalid, but let's handle it anyway)
majorVersion = versionString.toInt();
minorVersion = 0; // ### TODO: Check with magic version numbers above
} else {
majorVersion = versionString.left(dotIdx).toInt();
minorVersion = versionString.mid(dotIdx + 1).toInt();
}
}
#ifndef NO_DECLARATIVE_BACKEND
foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
namespaceObject->setProperty(object->qmlTypeName(), object);
}
#endif // NO_DECLARATIVE_BACKEND
} else if (ast->fileName) {
QString path = _doc->path(); QString path = _doc->path();
path += QLatin1Char('/'); path += QLatin1Char('/');
path += ast->fileName->asString(); path += ast->fileName->asString();
@@ -488,7 +313,7 @@ bool Bind::visit(FunctionDeclaration *ast)
if (_currentObjectValue->property(ast->name->asString())) if (_currentObjectValue->property(ast->name->asString()))
return false; return false;
ASTFunctionValue *function = new ASTFunctionValue(ast, _interp); ASTFunctionValue *function = new ASTFunctionValue(ast, &_interp);
_currentObjectValue->setProperty(ast->name->asString(), function); _currentObjectValue->setProperty(ast->name->asString(), function);
return false; // ### eventually want to visit function bodies return false; // ### eventually want to visit function bodies
} }

View File

@@ -31,11 +31,12 @@
#define QMLBIND_H #define QMLBIND_H
#include <qmljs/parser/qmljsastvisitor_p.h> #include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljsdocument.h>
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QSharedPointer>
namespace QmlJS { namespace QmlJS {
@@ -44,21 +45,13 @@ class Link;
class QMLJS_EXPORT Bind: protected AST::Visitor class QMLJS_EXPORT Bind: protected AST::Visitor
{ {
protected:
Bind(Document::Ptr doc, Interpreter::Engine *interp);
public: public:
Bind(Document *doc);
virtual ~Bind(); virtual ~Bind();
QStringList includedScripts() const; QStringList includedScripts() const;
QStringList localImports() const; QStringList localImports() const;
// ### TODO: This methods should go. Bind each document after parsing, link later.
static Interpreter::ObjectValue *scopeChainAt(Document::Ptr currentDocument,
const Snapshot &snapshot,
Interpreter::Engine *interp,
AST::UiObjectMember *currentObject);
protected: protected:
using AST::Visitor::visit; using AST::Visitor::visit;
@@ -83,11 +76,10 @@ protected:
Interpreter::ObjectValue *bindObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer); Interpreter::ObjectValue *bindObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer);
private: private:
Document::Ptr _doc; Document *_doc;
Interpreter::Engine *_interp; Interpreter::Engine _interp;
Interpreter::ObjectValue *_currentObjectValue; Interpreter::ObjectValue *_currentObjectValue;
Interpreter::ObjectValue *_typeEnvironment;
Interpreter::ObjectValue *_idEnvironment; Interpreter::ObjectValue *_idEnvironment;
Interpreter::ObjectValue *_functionEnvironment; Interpreter::ObjectValue *_functionEnvironment;
Interpreter::ObjectValue *_rootObjectValue; Interpreter::ObjectValue *_rootObjectValue;
@@ -100,6 +92,7 @@ private:
friend class LinkImports; friend class LinkImports;
friend class Link; friend class Link;
}; };
typedef QSharedPointer<Bind> BindPtr;
} // end of namespace Qml } // end of namespace Qml

View File

@@ -28,6 +28,7 @@
**************************************************************************/ **************************************************************************/
#include "qmljsdocument.h" #include "qmljsdocument.h"
#include "qmljsbind.h"
#include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljslexer_p.h> #include <qmljs/parser/qmljslexer_p.h>
#include <qmljs/parser/qmljsparser_p.h> #include <qmljs/parser/qmljsparser_p.h>
@@ -143,6 +144,8 @@ bool Document::parseQml()
_ast = parser.ast(); _ast = parser.ast();
_diagnosticMessages = parser.diagnosticMessages(); _diagnosticMessages = parser.diagnosticMessages();
_bind = BindPtr(new Bind(this));
return _parsedCorrectly; return _parsedCorrectly;
} }
@@ -164,6 +167,8 @@ bool Document::parseJavaScript()
_ast = cast<Program*>(parser.rootNode()); _ast = cast<Program*>(parser.rootNode());
_diagnosticMessages = parser.diagnosticMessages(); _diagnosticMessages = parser.diagnosticMessages();
_bind = BindPtr(new Bind(this));
return _parsedCorrectly; return _parsedCorrectly;
} }
@@ -190,6 +195,11 @@ bool Document::parseExpression()
return _parsedCorrectly; return _parsedCorrectly;
} }
BindPtr Document::bind() const
{
return _bind;
}
Snapshot::Snapshot() Snapshot::Snapshot()
{ {
} }

View File

@@ -40,6 +40,9 @@
namespace QmlJS { namespace QmlJS {
class Bind;
typedef QSharedPointer<Bind> BindPtr;
class QMLJS_EXPORT Document class QMLJS_EXPORT Document
{ {
public: public:
@@ -71,6 +74,8 @@ public:
bool isParsedCorrectly() const bool isParsedCorrectly() const
{ return _parsedCorrectly; } { return _parsedCorrectly; }
BindPtr bind() const;
int documentRevision() const; int documentRevision() const;
void setDocumentRevision(int documentRevision); void setDocumentRevision(int documentRevision);
@@ -89,6 +94,7 @@ private:
QString _path; QString _path;
QString _componentName; QString _componentName;
QString _source; QString _source;
BindPtr _bind;
}; };
class QMLJS_EXPORT Snapshot class QMLJS_EXPORT Snapshot

View File

@@ -12,6 +12,124 @@ using namespace QmlJS;
using namespace QmlJS::Interpreter; using namespace QmlJS::Interpreter;
using namespace QmlJS::AST; using namespace QmlJS::AST;
Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engine *interp)
: _snapshot(snapshot)
, _interp(interp)
{
_docs = reachableDocuments(currentDoc, snapshot);
linkImports();
}
Link::~Link()
{
// unset all prototypes and scopes
foreach (Document::Ptr doc, _docs) {
BindPtr bind = doc->bind();
if (doc->qmlProgram()) {
bind->_idEnvironment->setScope(0);
bind->_functionEnvironment->setScope(0);
foreach (ObjectValue *object, bind->_qmlObjectBindings) {
object->setPrototype(0);
object->setScope(0);
}
foreach (ObjectValue *object, bind->_qmlObjectDefinitions) {
object->setPrototype(0);
object->setScope(0);
}
} else if (doc->jsProgram()) {
bind->_rootObjectValue->setScope(0);
}
}
}
static ObjectValue *pushScope(ObjectValue *next, ObjectValue *onto)
{
onto->setScope(next);
return next;
}
ObjectValue *Link::scopeChainAt(Document::Ptr doc, UiObjectMember *currentObject)
{
BindPtr bind = doc->bind();
ObjectValue *scopeObject;
if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
scopeObject = bind->_qmlObjectDefinitions.value(definition);
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject))
scopeObject = bind->_qmlObjectBindings.value(binding);
else
return bind->_interp.globalObject();
if (!scopeObject)
return bind->_interp.globalObject();
// Build the scope chain.
ObjectValue *scopeStart = _typeEnvironments.value(doc.data());
ObjectValue *scope = scopeStart;
scope = pushScope(bind->_idEnvironment, scope);
scope = pushScope(bind->_functionEnvironment, scope);
foreach (const QString &scriptFile, doc->bind()->includedScripts()) {
if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
if (scriptDoc->jsProgram()) {
scope = pushScope(scriptDoc->bind()->_rootObjectValue, scope);
}
}
}
scope = pushScope(scopeObject, scope);
if (scopeObject != bind->_rootObjectValue)
scope = pushScope(bind->_rootObjectValue, scope);
scope = pushScope(bind->_interp.globalObject(), scope);
// May want to link to instantiating components from here.
return scopeStart;
}
void Link::linkImports()
{
foreach (Document::Ptr doc, _docs) {
BindPtr bind = doc->bind();
ObjectValue *typeEnv = _interp->newObject(/*prototype =*/0);
_typeEnvironments.insert(doc.data(), typeEnv);
// Populate the _typeEnvironment with imports.
populateImportedTypes(typeEnv, doc);
// Set the prototypes.
{
QHash<UiObjectDefinition *, ObjectValue *>::iterator it = bind->_qmlObjectDefinitions.begin();
QHash<UiObjectDefinition *, ObjectValue *>::iterator end = bind->_qmlObjectDefinitions.end();
for (; it != end; ++it) {
UiObjectDefinition *key = it.key();
ObjectValue *value = it.value();
if (!key->qualifiedTypeNameId)
continue;
value->setPrototype(lookupType(typeEnv, key->qualifiedTypeNameId));
}
}
{
QHash<UiObjectBinding *, ObjectValue *>::iterator it = bind->_qmlObjectBindings.begin();
QHash<UiObjectBinding *, ObjectValue *>::iterator end = bind->_qmlObjectBindings.end();
for (; it != end; ++it) {
UiObjectBinding *key = it.key();
ObjectValue *value = it.value();
if (!key->qualifiedTypeNameId)
continue;
value->setPrototype(lookupType(typeEnv, key->qualifiedTypeNameId));
}
}
}
}
static QString componentName(const QString &fileName) static QString componentName(const QString &fileName)
{ {
QString componentName = fileName; QString componentName = fileName;
@@ -22,11 +140,8 @@ static QString componentName(const QString &fileName)
return componentName; return componentName;
} }
void LinkImports::linkImports(Bind *bind, const QList<Bind *> &binds) void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc)
{ {
// ### TODO: remove all properties from _typeEnv
Document::Ptr doc = bind->_doc;
if (! (doc->qmlProgram() && doc->qmlProgram()->imports)) if (! (doc->qmlProgram() && doc->qmlProgram()->imports))
return; return;
@@ -35,65 +150,127 @@ void LinkImports::linkImports(Bind *bind, const QList<Bind *> &binds)
// implicit imports: // implicit imports:
// qml files in the same directory are available without explicit imports // qml files in the same directory are available without explicit imports
foreach (Bind *otherBind, binds) { foreach (Document::Ptr otherDoc, _docs) {
if (otherBind == bind) if (otherDoc == doc)
continue; continue;
Document::Ptr otherDoc = otherBind->_doc;
QFileInfo otherFileInfo(otherDoc->fileName()); QFileInfo otherFileInfo(otherDoc->fileName());
const QString otherAbsolutePath = otherFileInfo.absolutePath(); const QString otherAbsolutePath = otherFileInfo.absolutePath();
if (otherAbsolutePath != absolutePath) if (otherAbsolutePath != absolutePath)
continue; continue;
bind->_typeEnvironment->setProperty(componentName(otherFileInfo.fileName()), typeEnv->setProperty(componentName(otherFileInfo.fileName()),
otherBind->_rootObjectValue); otherDoc->bind()->_rootObjectValue);
} }
// explicit imports, whether directories or files // explicit imports, whether directories or files
for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) { for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) {
if (! (it->import && it->import->fileName)) if (! it->import)
continue; continue;
QString path = absolutePath; if (it->import->fileName) {
path += QLatin1Char('/'); importFile(typeEnv, doc, it->import, absolutePath);
path += it->import->fileName->asString(); } else if (it->import->importUri) {
path = QDir::cleanPath(path); importNonFile(typeEnv, doc, it->import);
ObjectValue *importNamespace = 0;
foreach (Bind *otherBind, binds) {
Document::Ptr otherDoc = otherBind->_doc;
QFileInfo otherFileInfo(otherDoc->fileName());
const QString otherAbsolutePath = otherFileInfo.absolutePath();
bool directoryImport = (path == otherAbsolutePath);
bool fileImport = (path == otherDoc->fileName());
if (!directoryImport && !fileImport)
continue;
if (directoryImport && it->import->importId && !importNamespace) {
importNamespace = bind->_interp->newObject(/*prototype =*/0);
bind->_typeEnvironment->setProperty(it->import->importId->asString(), importNamespace);
}
QString targetName;
if (fileImport && it->import->importId) {
targetName = it->import->importId->asString();
} else {
targetName = componentName(otherFileInfo.fileName());
}
ObjectValue *importInto = bind->_typeEnvironment;
if (importNamespace)
importInto = importNamespace;
importInto->setProperty(targetName, otherBind->_rootObjectValue);
} }
} }
} }
static const ObjectValue *lookupType(ObjectValue *env, UiQualifiedId *id) /*
import "content"
import "content" as Xxx
import "content" 4.6
import "content" 4.6 as Xxx
import "http://www.ovi.com/" as Ovi
*/
void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
AST::UiImport *import, const QString &startPath)
{
if (!import->fileName)
return;
QString path = startPath;
path += QLatin1Char('/');
path += import->fileName->asString();
path = QDir::cleanPath(path);
ObjectValue *importNamespace = 0;
foreach (Document::Ptr otherDoc, _docs) {
QFileInfo otherFileInfo(otherDoc->fileName());
const QString otherAbsolutePath = otherFileInfo.absolutePath();
bool directoryImport = (path == otherAbsolutePath);
bool fileImport = (path == otherDoc->fileName());
if (!directoryImport && !fileImport)
continue;
if (directoryImport && import->importId && !importNamespace) {
importNamespace = _interp->newObject(/*prototype =*/0);
typeEnv->setProperty(import->importId->asString(), importNamespace);
}
QString targetName;
if (fileImport && import->importId) {
targetName = import->importId->asString();
} else {
targetName = componentName(otherFileInfo.fileName());
}
ObjectValue *importInto = typeEnv;
if (importNamespace)
importInto = importNamespace;
importInto->setProperty(targetName, otherDoc->bind()->_rootObjectValue);
}
}
/*
import Qt 4.6
import Qt 4.6 as Xxx
(import com.nokia.qt is the same as the ones above)
*/
void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, AST::UiImport *import)
{
ObjectValue *namespaceObject = 0;
if (import->importId) { // with namespace we insert an object in the type env. to hold the imported types
namespaceObject = _interp->newObject(/*prototype */ 0);
typeEnv->setProperty(import->importId->asString(), namespaceObject);
} else { // without namespace we insert all types directly into the type env.
namespaceObject = typeEnv;
}
// try the metaobject system
if (import->importUri) {
const QString package = Bind::toString(import->importUri, '/');
int majorVersion = -1; // ### TODO: Check these magic version numbers
int minorVersion = -1; // ### TODO: Check these magic version numbers
if (import->versionToken.isValid()) {
const QString versionString = doc->source().mid(import->versionToken.offset, import->versionToken.length);
const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1) {
// only major (which is probably invalid, but let's handle it anyway)
majorVersion = versionString.toInt();
minorVersion = 0; // ### TODO: Check with magic version numbers above
} else {
majorVersion = versionString.left(dotIdx).toInt();
minorVersion = versionString.mid(dotIdx + 1).toInt();
}
}
#ifndef NO_DECLARATIVE_BACKEND
foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
namespaceObject->setProperty(object->qmlTypeName(), object);
}
#endif // NO_DECLARATIVE_BACKEND
}
}
const ObjectValue *Link::lookupType(ObjectValue *env, UiQualifiedId *id)
{ {
const ObjectValue *objectValue = env; const ObjectValue *objectValue = env;
@@ -111,65 +288,37 @@ static const ObjectValue *lookupType(ObjectValue *env, UiQualifiedId *id)
return objectValue; return objectValue;
} }
void LinkImports::operator()(const QList<Bind *> &binds) QList<Document::Ptr> Link::reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot)
{ {
foreach (Bind *bind, binds) { QList<Document::Ptr> docs;
// Populate the _typeEnvironment with imports.
linkImports(bind, binds);
// Set the prototypes. QSet<QString> processed;
{ QStringList todo;
QHash<UiObjectDefinition *, ObjectValue *>::iterator it = bind->_qmlObjectDefinitions.begin();
QHash<UiObjectDefinition *, ObjectValue *>::iterator end = bind->_qmlObjectDefinitions.end();
for (; it != end; ++it) {
UiObjectDefinition *key = it.key();
ObjectValue *value = it.value();
if (!key->qualifiedTypeNameId)
continue;
value->setPrototype(lookupType(bind->_typeEnvironment, key->qualifiedTypeNameId)); QMultiHash<QString, Document::Ptr> documentByPath;
} foreach (Document::Ptr doc, snapshot)
documentByPath.insert(doc->path(), doc);
todo.append(startDoc->path());
// Find the reachable documents.
while (! todo.isEmpty()) {
const QString path = todo.takeFirst();
if (processed.contains(path))
continue;
processed.insert(path);
QStringList localImports;
foreach (Document::Ptr doc, documentByPath.values(path)) {
docs += doc;
localImports += doc->bind()->localImports();
} }
{
QHash<UiObjectBinding *, ObjectValue *>::iterator it = bind->_qmlObjectBindings.begin();
QHash<UiObjectBinding *, ObjectValue *>::iterator end = bind->_qmlObjectBindings.end();
for (; it != end; ++it) {
UiObjectBinding *key = it.key();
ObjectValue *value = it.value();
if (!key->qualifiedTypeNameId)
continue;
value->setPrototype(lookupType(bind->_typeEnvironment, key->qualifiedTypeNameId)); localImports.removeDuplicates();
} todo += localImports;
}
} }
}
return docs;
ObjectValue *Link::operator()(const QList<Bind *> &binds, Bind *currentBind, UiObjectMember *currentObject)
{
Q_UNUSED(binds);
ObjectValue *scopeObject;
if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
scopeObject = currentBind->_qmlObjectDefinitions.value(definition);
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject))
scopeObject = currentBind->_qmlObjectBindings.value(binding);
else
return currentBind->_interp->globalObject();
if (!scopeObject)
return currentBind->_interp->globalObject();
// Build the scope chain.
currentBind->_typeEnvironment->setScope(currentBind->_idEnvironment);
currentBind->_idEnvironment->setScope(currentBind->_functionEnvironment);
currentBind->_functionEnvironment->setScope(scopeObject);
if (scopeObject != currentBind->_rootObjectValue) {
scopeObject->setScope(currentBind->_rootObjectValue);
currentBind->_rootObjectValue->setScope(currentBind->_interp->globalObject());
} else {
scopeObject->setScope(currentBind->_interp->globalObject());
}
// May want to link to instantiating components from here.
return currentBind->_typeEnvironment;
} }

View File

@@ -1,29 +1,49 @@
#ifndef QMLJSLINK_H #ifndef QMLJSLINK_H
#define QMLJSLINK_H #define QMLJSLINK_H
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljsinterpreter.h>
#include <qmljs/parser/qmljsastfwd_p.h> #include <qmljs/parser/qmljsastfwd_p.h>
#include <qmljs/parser/qmljsengine_p.h> #include <qmljs/parser/qmljsengine_p.h>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QHash>
namespace QmlJS { namespace QmlJS {
class Bind; /*
Temporarily links a set of bound documents together to allow resolving cross-document
class LinkImports dependencies. The link is undone when this object is destoyed.
*/
class QMLJS_EXPORT Link
{ {
public: public:
void operator()(const QList<Bind *> &binds); // Link all documents in snapshot reachable from doc.
Link(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp);
~Link();
// Get the scope chain for the currentObject inside doc.
Interpreter::ObjectValue *scopeChainAt(Document::Ptr doc, AST::UiObjectMember *currentObject);
private: private:
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId* targetNamespace); static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
void linkImports(Bind *bind, const QList<Bind *> &binds); static const Interpreter::ObjectValue *lookupType(Interpreter::ObjectValue *env, AST::UiQualifiedId *id);
};
class Link void linkImports();
{
public: void populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc);
Interpreter::ObjectValue *operator()(const QList<Bind *> &binds, Bind *currentBind, AST::UiObjectMember *currentObject); void importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
AST::UiImport *import, const QString &startPath);
void importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
AST::UiImport *import);
void importObject(BindPtr bind, const QString &name, Interpreter::ObjectValue *object, NameId* targetNamespace);
private:
Snapshot _snapshot;
Interpreter::Engine *_interp;
QList<Document::Ptr> _docs;
QHash<Document *, Interpreter::ObjectValue *> _typeEnvironments;
}; };
} // namespace QmlJS } // namespace QmlJS

View File

@@ -632,10 +632,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
// Set up the current scope chain. // Set up the current scope chain.
Interpreter::ObjectValue *scope = interp.globalObject(); Interpreter::ObjectValue *scope = interp.globalObject();
Link link(qmlDocument, snapshot, &interp);
if (isQmlFile && qmlDocument) { if (isQmlFile && qmlDocument) {
AST::UiObjectMember *declaringMember = semanticInfo.declaringMember(editor->position()); AST::UiObjectMember *declaringMember = semanticInfo.declaringMember(editor->position());
scope = Bind::scopeChainAt(qmlDocument, snapshot, &interp, declaringMember); scope = link.scopeChainAt(qmlDocument, declaringMember);
} }
// Search for the operator that triggered the completion. // Search for the operator that triggered the completion.

View File

@@ -37,6 +37,7 @@
#include <debugger/debuggerconstants.h> #include <debugger/debuggerconstants.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <qmljs/qmljsbind.h> #include <qmljs/qmljsbind.h>
#include <qmljs/qmljslink.h>
#include <qmljs/qmljscheck.h> #include <qmljs/qmljscheck.h>
#include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljsinterpreter.h>
#include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsast_p.h>
@@ -171,7 +172,8 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
AST::UiObjectMember *declaringMember = semanticInfo.declaringMember(pos); AST::UiObjectMember *declaringMember = semanticInfo.declaringMember(pos);
Interpreter::Engine interp; Interpreter::Engine interp;
const Interpreter::ObjectValue *scope = Bind::scopeChainAt(qmlDocument, snapshot, &interp, declaringMember); Link link(qmlDocument, snapshot, &interp);
const Interpreter::ObjectValue *scope = link.scopeChainAt(qmlDocument, declaringMember);
Check check(&interp); Check check(&interp);
const Interpreter::Value *value = check(node, scope); const Interpreter::Value *value = check(node, scope);