forked from qt-creator/qt-creator
Bind each QML document after parsing. Link them before use.
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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.
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user