2010-01-28 14:53:53 +01:00
|
|
|
#include "qmljslink.h"
|
|
|
|
|
|
|
|
#include "parser/qmljsast_p.h"
|
|
|
|
#include "qmljsdocument.h"
|
|
|
|
#include "qmljsbind.h"
|
|
|
|
|
|
|
|
#include <QtCore/QFileInfo>
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
|
|
|
using namespace QmlJS;
|
|
|
|
using namespace QmlJS::Interpreter;
|
|
|
|
using namespace QmlJS::AST;
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engine *interp)
|
|
|
|
: _snapshot(snapshot)
|
2010-02-03 10:24:25 +01:00
|
|
|
, _context(interp)
|
2010-02-02 15:55:17 +01:00
|
|
|
{
|
|
|
|
_docs = reachableDocuments(currentDoc, snapshot);
|
|
|
|
|
|
|
|
linkImports();
|
|
|
|
}
|
|
|
|
|
|
|
|
Link::~Link()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-02-03 10:59:52 +01:00
|
|
|
Context *Link::context()
|
2010-02-02 15:55:17 +01:00
|
|
|
{
|
2010-02-03 10:59:52 +01:00
|
|
|
return &_context;
|
2010-02-02 15:55:17 +01:00
|
|
|
}
|
|
|
|
|
2010-02-03 10:59:52 +01:00
|
|
|
Interpreter::Engine *Link::engine()
|
2010-02-02 15:55:17 +01:00
|
|
|
{
|
2010-02-03 14:31:03 +01:00
|
|
|
return _context.engine();
|
2010-02-03 10:59:52 +01:00
|
|
|
}
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 10:59:52 +01:00
|
|
|
void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
|
|
|
|
{
|
2010-02-03 14:31:03 +01:00
|
|
|
_context.pushScope(engine()->globalObject());
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
if (! doc)
|
2010-02-03 10:59:52 +01:00
|
|
|
return;
|
2010-02-03 14:31:03 +01:00
|
|
|
|
|
|
|
if (doc->qmlProgram() != 0)
|
|
|
|
_context.setLookupMode(Context::QmlLookup);
|
2010-02-03 10:59:52 +01:00
|
|
|
|
2010-02-03 15:59:15 +01:00
|
|
|
Bind *bind = doc->bind();
|
2010-02-02 15:55:17 +01:00
|
|
|
|
|
|
|
// Build the scope chain.
|
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
// ### FIXME: May want to link to instantiating components from here.
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
if (bind->_rootObjectValue)
|
|
|
|
_context.pushScope(bind->_rootObjectValue);
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
ObjectValue *scopeObject = 0;
|
|
|
|
if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
|
|
|
|
scopeObject = bind->_qmlObjects.value(definition);
|
|
|
|
else if (UiObjectBinding *binding = cast<UiObjectBinding *>(currentObject))
|
|
|
|
scopeObject = bind->_qmlObjects.value(binding);
|
2010-02-03 10:59:52 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
if (scopeObject && scopeObject != bind->_rootObjectValue)
|
|
|
|
_context.pushScope(scopeObject);
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
const QStringList &includedScripts = bind->includedScripts();
|
|
|
|
for (int index = includedScripts.size() - 1; index != -1; --index) {
|
|
|
|
const QString &scriptFile = includedScripts.at(index);
|
2010-02-03 10:59:52 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
|
|
|
|
if (scriptDoc->jsProgram()) {
|
|
|
|
_context.pushScope(scriptDoc->bind()->_rootObjectValue);
|
|
|
|
}
|
2010-02-03 10:59:52 +01:00
|
|
|
}
|
|
|
|
}
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-03 14:31:03 +01:00
|
|
|
if (bind->_functionEnvironment)
|
|
|
|
_context.pushScope(bind->_functionEnvironment);
|
|
|
|
|
|
|
|
if (bind->_idEnvironment)
|
|
|
|
_context.pushScope(bind->_idEnvironment);
|
|
|
|
|
2010-02-03 15:39:57 +01:00
|
|
|
if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data())) {
|
2010-02-03 14:31:03 +01:00
|
|
|
_context.pushScope(typeEnvironment);
|
2010-02-03 15:39:57 +01:00
|
|
|
_context.setTypeEnvironment(typeEnvironment);
|
|
|
|
}
|
2010-02-02 15:55:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Link::linkImports()
|
|
|
|
{
|
|
|
|
foreach (Document::Ptr doc, _docs) {
|
2010-02-03 14:31:03 +01:00
|
|
|
ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME
|
2010-02-02 15:55:17 +01:00
|
|
|
_typeEnvironments.insert(doc.data(), typeEnv);
|
|
|
|
|
|
|
|
// Populate the _typeEnvironment with imports.
|
|
|
|
populateImportedTypes(typeEnv, doc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-28 14:53:53 +01:00
|
|
|
static QString componentName(const QString &fileName)
|
|
|
|
{
|
|
|
|
QString componentName = fileName;
|
|
|
|
int dotIndex = componentName.indexOf(QLatin1Char('.'));
|
|
|
|
if (dotIndex != -1)
|
|
|
|
componentName.truncate(dotIndex);
|
|
|
|
componentName[0] = componentName[0].toUpper();
|
|
|
|
return componentName;
|
|
|
|
}
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc)
|
2010-01-28 14:53:53 +01:00
|
|
|
{
|
|
|
|
if (! (doc->qmlProgram() && doc->qmlProgram()->imports))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QFileInfo fileInfo(doc->fileName());
|
|
|
|
const QString absolutePath = fileInfo.absolutePath();
|
|
|
|
|
2010-01-29 13:21:50 +01:00
|
|
|
// implicit imports:
|
|
|
|
// qml files in the same directory are available without explicit imports
|
2010-02-02 15:55:17 +01:00
|
|
|
foreach (Document::Ptr otherDoc, _docs) {
|
|
|
|
if (otherDoc == doc)
|
2010-01-28 14:53:53 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
QFileInfo otherFileInfo(otherDoc->fileName());
|
|
|
|
const QString otherAbsolutePath = otherFileInfo.absolutePath();
|
|
|
|
|
|
|
|
if (otherAbsolutePath != absolutePath)
|
|
|
|
continue;
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
typeEnv->setProperty(componentName(otherFileInfo.fileName()),
|
|
|
|
otherDoc->bind()->_rootObjectValue);
|
2010-01-28 14:53:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// explicit imports, whether directories or files
|
|
|
|
for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) {
|
2010-02-02 15:55:17 +01:00
|
|
|
if (! it->import)
|
2010-01-28 14:53:53 +01:00
|
|
|
continue;
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
if (it->import->fileName) {
|
|
|
|
importFile(typeEnv, doc, it->import, absolutePath);
|
|
|
|
} else if (it->import->importUri) {
|
|
|
|
importNonFile(typeEnv, doc, it->import);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
/*
|
|
|
|
import "content"
|
|
|
|
import "content" as Xxx
|
|
|
|
import "content" 4.6
|
|
|
|
import "content" 4.6 as Xxx
|
2010-01-29 13:36:07 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
import "http://www.ovi.com/" as Ovi
|
|
|
|
*/
|
|
|
|
void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc,
|
|
|
|
AST::UiImport *import, const QString &startPath)
|
|
|
|
{
|
2010-02-02 16:36:14 +01:00
|
|
|
Q_UNUSED(doc)
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
if (!import->fileName)
|
|
|
|
return;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
QString path = startPath;
|
|
|
|
path += QLatin1Char('/');
|
|
|
|
path += import->fileName->asString();
|
|
|
|
path = QDir::cleanPath(path);
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
ObjectValue *importNamespace = 0;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
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;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
if (directoryImport && import->importId && !importNamespace) {
|
2010-02-03 14:31:03 +01:00
|
|
|
importNamespace = engine()->newObject(/*prototype =*/0);
|
2010-02-02 15:55:17 +01:00
|
|
|
typeEnv->setProperty(import->importId->asString(), importNamespace);
|
|
|
|
}
|
2010-01-29 13:36:07 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
QString targetName;
|
|
|
|
if (fileImport && import->importId) {
|
|
|
|
targetName = import->importId->asString();
|
|
|
|
} else {
|
|
|
|
targetName = componentName(otherFileInfo.fileName());
|
2010-01-28 14:53:53 +01:00
|
|
|
}
|
2010-02-02 15:55:17 +01:00
|
|
|
|
|
|
|
ObjectValue *importInto = typeEnv;
|
|
|
|
if (importNamespace)
|
|
|
|
importInto = importNamespace;
|
|
|
|
|
|
|
|
importInto->setProperty(targetName, otherDoc->bind()->_rootObjectValue);
|
2010-01-28 14:53:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
/*
|
|
|
|
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
|
2010-02-03 14:31:03 +01:00
|
|
|
namespaceObject = engine()->newObject(/*prototype */ 0);
|
2010-02-02 15:55:17 +01:00
|
|
|
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
|
2010-02-03 14:31:03 +01:00
|
|
|
foreach (QmlObjectValue *object, engine()->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
|
2010-02-02 15:55:17 +01:00
|
|
|
namespaceObject->setProperty(object->qmlTypeName(), object);
|
|
|
|
}
|
|
|
|
#endif // NO_DECLARATIVE_BACKEND
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-02 17:06:48 +01:00
|
|
|
UiQualifiedId *Link::qualifiedTypeNameId(Node *node)
|
|
|
|
{
|
|
|
|
if (UiObjectBinding *binding = AST::cast<UiObjectBinding *>(node))
|
|
|
|
return binding->qualifiedTypeNameId;
|
|
|
|
else if (UiObjectDefinition *binding = AST::cast<UiObjectDefinition *>(node))
|
|
|
|
return binding->qualifiedTypeNameId;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
QList<Document::Ptr> Link::reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot)
|
2010-01-28 14:53:53 +01:00
|
|
|
{
|
2010-02-02 15:55:17 +01:00
|
|
|
QList<Document::Ptr> docs;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
QSet<QString> processed;
|
|
|
|
QStringList todo;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
QMultiHash<QString, Document::Ptr> documentByPath;
|
|
|
|
foreach (Document::Ptr doc, snapshot)
|
|
|
|
documentByPath.insert(doc->path(), doc);
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
todo.append(startDoc->path());
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
// Find the reachable documents.
|
|
|
|
while (! todo.isEmpty()) {
|
|
|
|
const QString path = todo.takeFirst();
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
if (processed.contains(path))
|
|
|
|
continue;
|
2010-01-28 14:53:53 +01:00
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
processed.insert(path);
|
|
|
|
|
|
|
|
QStringList localImports;
|
|
|
|
foreach (Document::Ptr doc, documentByPath.values(path)) {
|
|
|
|
docs += doc;
|
|
|
|
localImports += doc->bind()->localImports();
|
|
|
|
}
|
|
|
|
|
|
|
|
localImports.removeDuplicates();
|
|
|
|
todo += localImports;
|
2010-01-28 14:53:53 +01:00
|
|
|
}
|
|
|
|
|
2010-02-02 15:55:17 +01:00
|
|
|
return docs;
|
2010-01-28 14:53:53 +01:00
|
|
|
}
|