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
|
||||
|
||||
Bind::Bind(Document::Ptr doc, Interpreter::Engine *interp)
|
||||
Bind::Bind(Document *doc)
|
||||
: _doc(doc),
|
||||
_interp(interp),
|
||||
_currentObjectValue(0),
|
||||
_typeEnvironment(0),
|
||||
_idEnvironment(0),
|
||||
_functionEnvironment(0),
|
||||
_rootObjectValue(0)
|
||||
{
|
||||
if (_doc && _doc->qmlProgram() != 0) {
|
||||
UiProgram *program = _doc->qmlProgram();
|
||||
if (!_doc)
|
||||
return;
|
||||
|
||||
_currentObjectValue = 0;
|
||||
_typeEnvironment = _interp->newObject(/*prototype =*/ 0);
|
||||
_idEnvironment = _interp->newObject(/*prototype =*/ 0);
|
||||
_functionEnvironment = _interp->newObject(/*prototype =*/ 0);
|
||||
_rootObjectValue = 0;
|
||||
|
||||
_qmlObjectDefinitions.clear();
|
||||
_qmlObjectBindings.clear();
|
||||
|
||||
accept(program);
|
||||
if (_doc->qmlProgram()) {
|
||||
_idEnvironment = _interp.newObject(/*prototype =*/ 0);
|
||||
_functionEnvironment = _interp.newObject(/*prototype =*/ 0);
|
||||
} else if (_doc->jsProgram()) {
|
||||
_currentObjectValue = _interp.globalObject();
|
||||
_rootObjectValue = _interp.globalObject();
|
||||
}
|
||||
|
||||
accept(_doc->ast());
|
||||
}
|
||||
|
||||
Bind::~Bind()
|
||||
@@ -214,77 +165,6 @@ ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue)
|
||||
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 result;
|
||||
@@ -350,7 +230,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
|
||||
}
|
||||
|
||||
// normal component instance
|
||||
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _interp);
|
||||
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp);
|
||||
parentObjectValue = switchObjectValue(objectValue);
|
||||
|
||||
if (parentObjectValue)
|
||||
@@ -368,64 +248,9 @@ void Bind::accept(Node *node)
|
||||
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)
|
||||
{
|
||||
if (! (ast->importUri || 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) {
|
||||
if (ast->fileName) {
|
||||
QString path = _doc->path();
|
||||
path += QLatin1Char('/');
|
||||
path += ast->fileName->asString();
|
||||
@@ -488,7 +313,7 @@ bool Bind::visit(FunctionDeclaration *ast)
|
||||
if (_currentObjectValue->property(ast->name->asString()))
|
||||
return false;
|
||||
|
||||
ASTFunctionValue *function = new ASTFunctionValue(ast, _interp);
|
||||
ASTFunctionValue *function = new ASTFunctionValue(ast, &_interp);
|
||||
_currentObjectValue->setProperty(ast->name->asString(), function);
|
||||
return false; // ### eventually want to visit function bodies
|
||||
}
|
||||
|
@@ -31,11 +31,12 @@
|
||||
#define QMLBIND_H
|
||||
|
||||
#include <qmljs/parser/qmljsastvisitor_p.h>
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
@@ -44,21 +45,13 @@ class Link;
|
||||
|
||||
class QMLJS_EXPORT Bind: protected AST::Visitor
|
||||
{
|
||||
protected:
|
||||
Bind(Document::Ptr doc, Interpreter::Engine *interp);
|
||||
|
||||
public:
|
||||
Bind(Document *doc);
|
||||
virtual ~Bind();
|
||||
|
||||
QStringList includedScripts() 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:
|
||||
using AST::Visitor::visit;
|
||||
|
||||
@@ -83,11 +76,10 @@ protected:
|
||||
Interpreter::ObjectValue *bindObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer);
|
||||
|
||||
private:
|
||||
Document::Ptr _doc;
|
||||
Interpreter::Engine *_interp;
|
||||
Document *_doc;
|
||||
Interpreter::Engine _interp;
|
||||
|
||||
Interpreter::ObjectValue *_currentObjectValue;
|
||||
Interpreter::ObjectValue *_typeEnvironment;
|
||||
Interpreter::ObjectValue *_idEnvironment;
|
||||
Interpreter::ObjectValue *_functionEnvironment;
|
||||
Interpreter::ObjectValue *_rootObjectValue;
|
||||
@@ -100,6 +92,7 @@ private:
|
||||
friend class LinkImports;
|
||||
friend class Link;
|
||||
};
|
||||
typedef QSharedPointer<Bind> BindPtr;
|
||||
|
||||
} // end of namespace Qml
|
||||
|
||||
|
@@ -28,6 +28,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "qmljsdocument.h"
|
||||
#include "qmljsbind.h"
|
||||
#include <qmljs/parser/qmljsast_p.h>
|
||||
#include <qmljs/parser/qmljslexer_p.h>
|
||||
#include <qmljs/parser/qmljsparser_p.h>
|
||||
@@ -143,6 +144,8 @@ bool Document::parseQml()
|
||||
_ast = parser.ast();
|
||||
_diagnosticMessages = parser.diagnosticMessages();
|
||||
|
||||
_bind = BindPtr(new Bind(this));
|
||||
|
||||
return _parsedCorrectly;
|
||||
}
|
||||
|
||||
@@ -164,6 +167,8 @@ bool Document::parseJavaScript()
|
||||
_ast = cast<Program*>(parser.rootNode());
|
||||
_diagnosticMessages = parser.diagnosticMessages();
|
||||
|
||||
_bind = BindPtr(new Bind(this));
|
||||
|
||||
return _parsedCorrectly;
|
||||
}
|
||||
|
||||
@@ -190,6 +195,11 @@ bool Document::parseExpression()
|
||||
return _parsedCorrectly;
|
||||
}
|
||||
|
||||
BindPtr Document::bind() const
|
||||
{
|
||||
return _bind;
|
||||
}
|
||||
|
||||
Snapshot::Snapshot()
|
||||
{
|
||||
}
|
||||
|
@@ -40,6 +40,9 @@
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class Bind;
|
||||
typedef QSharedPointer<Bind> BindPtr;
|
||||
|
||||
class QMLJS_EXPORT Document
|
||||
{
|
||||
public:
|
||||
@@ -71,6 +74,8 @@ public:
|
||||
bool isParsedCorrectly() const
|
||||
{ return _parsedCorrectly; }
|
||||
|
||||
BindPtr bind() const;
|
||||
|
||||
int documentRevision() const;
|
||||
void setDocumentRevision(int documentRevision);
|
||||
|
||||
@@ -89,6 +94,7 @@ private:
|
||||
QString _path;
|
||||
QString _componentName;
|
||||
QString _source;
|
||||
BindPtr _bind;
|
||||
};
|
||||
|
||||
class QMLJS_EXPORT Snapshot
|
||||
|
@@ -12,6 +12,124 @@ using namespace QmlJS;
|
||||
using namespace QmlJS::Interpreter;
|
||||
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)
|
||||
{
|
||||
QString componentName = fileName;
|
||||
@@ -22,11 +140,8 @@ static QString componentName(const QString &fileName)
|
||||
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))
|
||||
return;
|
||||
|
||||
@@ -35,35 +150,55 @@ void LinkImports::linkImports(Bind *bind, const QList<Bind *> &binds)
|
||||
|
||||
// implicit imports:
|
||||
// qml files in the same directory are available without explicit imports
|
||||
foreach (Bind *otherBind, binds) {
|
||||
if (otherBind == bind)
|
||||
foreach (Document::Ptr otherDoc, _docs) {
|
||||
if (otherDoc == doc)
|
||||
continue;
|
||||
|
||||
Document::Ptr otherDoc = otherBind->_doc;
|
||||
QFileInfo otherFileInfo(otherDoc->fileName());
|
||||
const QString otherAbsolutePath = otherFileInfo.absolutePath();
|
||||
|
||||
if (otherAbsolutePath != absolutePath)
|
||||
continue;
|
||||
|
||||
bind->_typeEnvironment->setProperty(componentName(otherFileInfo.fileName()),
|
||||
otherBind->_rootObjectValue);
|
||||
typeEnv->setProperty(componentName(otherFileInfo.fileName()),
|
||||
otherDoc->bind()->_rootObjectValue);
|
||||
}
|
||||
|
||||
// explicit imports, whether directories or files
|
||||
for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) {
|
||||
if (! (it->import && it->import->fileName))
|
||||
if (! it->import)
|
||||
continue;
|
||||
|
||||
QString path = absolutePath;
|
||||
if (it->import->fileName) {
|
||||
importFile(typeEnv, doc, it->import, absolutePath);
|
||||
} else if (it->import->importUri) {
|
||||
importNonFile(typeEnv, doc, it->import);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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 += it->import->fileName->asString();
|
||||
path += import->fileName->asString();
|
||||
path = QDir::cleanPath(path);
|
||||
|
||||
ObjectValue *importNamespace = 0;
|
||||
|
||||
foreach (Bind *otherBind, binds) {
|
||||
Document::Ptr otherDoc = otherBind->_doc;
|
||||
foreach (Document::Ptr otherDoc, _docs) {
|
||||
QFileInfo otherFileInfo(otherDoc->fileName());
|
||||
const QString otherAbsolutePath = otherFileInfo.absolutePath();
|
||||
|
||||
@@ -72,28 +207,70 @@ void LinkImports::linkImports(Bind *bind, const QList<Bind *> &binds)
|
||||
if (!directoryImport && !fileImport)
|
||||
continue;
|
||||
|
||||
if (directoryImport && it->import->importId && !importNamespace) {
|
||||
importNamespace = bind->_interp->newObject(/*prototype =*/0);
|
||||
bind->_typeEnvironment->setProperty(it->import->importId->asString(), importNamespace);
|
||||
if (directoryImport && import->importId && !importNamespace) {
|
||||
importNamespace = _interp->newObject(/*prototype =*/0);
|
||||
typeEnv->setProperty(import->importId->asString(), importNamespace);
|
||||
}
|
||||
|
||||
QString targetName;
|
||||
if (fileImport && it->import->importId) {
|
||||
targetName = it->import->importId->asString();
|
||||
if (fileImport && import->importId) {
|
||||
targetName = import->importId->asString();
|
||||
} else {
|
||||
targetName = componentName(otherFileInfo.fileName());
|
||||
}
|
||||
|
||||
ObjectValue *importInto = bind->_typeEnvironment;
|
||||
ObjectValue *importInto = typeEnv;
|
||||
if (importNamespace)
|
||||
importInto = importNamespace;
|
||||
|
||||
importInto->setProperty(targetName, otherBind->_rootObjectValue);
|
||||
}
|
||||
importInto->setProperty(targetName, otherDoc->bind()->_rootObjectValue);
|
||||
}
|
||||
}
|
||||
|
||||
static const ObjectValue *lookupType(ObjectValue *env, UiQualifiedId *id)
|
||||
/*
|
||||
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;
|
||||
|
||||
@@ -111,65 +288,37 @@ static const ObjectValue *lookupType(ObjectValue *env, UiQualifiedId *id)
|
||||
return objectValue;
|
||||
}
|
||||
|
||||
void LinkImports::operator()(const QList<Bind *> &binds)
|
||||
QList<Document::Ptr> Link::reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot)
|
||||
{
|
||||
foreach (Bind *bind, binds) {
|
||||
// Populate the _typeEnvironment with imports.
|
||||
linkImports(bind, binds);
|
||||
QList<Document::Ptr> docs;
|
||||
|
||||
// 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)
|
||||
QSet<QString> processed;
|
||||
QStringList todo;
|
||||
|
||||
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;
|
||||
|
||||
value->setPrototype(lookupType(bind->_typeEnvironment, 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;
|
||||
processed.insert(path);
|
||||
|
||||
value->setPrototype(lookupType(bind->_typeEnvironment, key->qualifiedTypeNameId));
|
||||
}
|
||||
QStringList localImports;
|
||||
foreach (Document::Ptr doc, documentByPath.values(path)) {
|
||||
docs += doc;
|
||||
localImports += doc->bind()->localImports();
|
||||
}
|
||||
|
||||
localImports.removeDuplicates();
|
||||
todo += localImports;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return docs;
|
||||
}
|
||||
|
@@ -1,29 +1,49 @@
|
||||
#ifndef QMLJSLINK_H
|
||||
#define QMLJSLINK_H
|
||||
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
#include <qmljs/qmljsbind.h>
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
#include <qmljs/parser/qmljsastfwd_p.h>
|
||||
#include <qmljs/parser/qmljsengine_p.h>
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QHash>
|
||||
|
||||
namespace QmlJS {
|
||||
|
||||
class Bind;
|
||||
|
||||
class LinkImports
|
||||
/*
|
||||
Temporarily links a set of bound documents together to allow resolving cross-document
|
||||
dependencies. The link is undone when this object is destoyed.
|
||||
*/
|
||||
class QMLJS_EXPORT Link
|
||||
{
|
||||
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:
|
||||
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId* targetNamespace);
|
||||
void linkImports(Bind *bind, const QList<Bind *> &binds);
|
||||
};
|
||||
static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
|
||||
static const Interpreter::ObjectValue *lookupType(Interpreter::ObjectValue *env, AST::UiQualifiedId *id);
|
||||
|
||||
class Link
|
||||
{
|
||||
public:
|
||||
Interpreter::ObjectValue *operator()(const QList<Bind *> &binds, Bind *currentBind, AST::UiObjectMember *currentObject);
|
||||
void linkImports();
|
||||
|
||||
void populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc);
|
||||
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
|
||||
|
@@ -632,10 +632,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
||||
|
||||
// Set up the current scope chain.
|
||||
Interpreter::ObjectValue *scope = interp.globalObject();
|
||||
Link link(qmlDocument, snapshot, &interp);
|
||||
|
||||
if (isQmlFile && qmlDocument) {
|
||||
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.
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <debugger/debuggerconstants.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <qmljs/qmljsbind.h>
|
||||
#include <qmljs/qmljslink.h>
|
||||
#include <qmljs/qmljscheck.h>
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
#include <qmljs/parser/qmljsast_p.h>
|
||||
@@ -171,7 +172,8 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
|
||||
AST::UiObjectMember *declaringMember = semanticInfo.declaringMember(pos);
|
||||
|
||||
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);
|
||||
const Interpreter::Value *value = check(node, scope);
|
||||
|
Reference in New Issue
Block a user