Commit 1d39377f authored by Christian Kamm's avatar Christian Kamm
Browse files

Separate bind into bind, link imports and building the scope chain.

parent 2ab90078
......@@ -17,7 +17,8 @@ HEADERS += \
$$PWD/qmljscheck.h \
$$PWD/qmljsdocument.h \
$$PWD/qmljsscanner.h \
$$PWD/qmljsinterpreter.h
$$PWD/qmljsinterpreter.h \
$$PWD/qmljslink.h
SOURCES += \
$$PWD/qmljsbind.cpp \
......@@ -25,7 +26,8 @@ SOURCES += \
$$PWD/qmljsdocument.cpp \
$$PWD/qmljsscanner.cpp \
$$PWD/qmljsinterpreter.cpp \
$$PWD/qmljsmetatypesystem.cpp
$$PWD/qmljsmetatypesystem.cpp \
$$PWD/qmljslink.cpp
contains(QT_CONFIG, declarative) {
QT += declarative
......
......@@ -29,6 +29,7 @@
#include "parser/qmljsast_p.h"
#include "qmljsbind.h"
#include "qmljslink.h"
#include "qmljsmetatypesystem.h"
#include <QtCore/QDebug>
......@@ -36,53 +37,60 @@ using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;
Bind::Bind(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp)
Bind::Bind(Document::Ptr doc, Interpreter::Engine *interp)
: _doc(doc),
_snapshot(snapshot),
_interp(interp),
_interestingMember(0),
_currentObjectValue(0),
_typeEnvironment(0),
_idEnvironment(0),
_functionEnvironment(0),
_interestingObjectValue(0),
_rootObjectValue(0)
{
(*this)();
}
Bind::~Bind()
{
}
Interpreter::ObjectValue *Bind::operator()(UiObjectMember *member)
void Bind::operator()()
{
UiProgram *program = _doc->qmlProgram();
if (!program)
return 0;
_interestingMember = member;
return;
_currentObjectValue = 0;
_typeEnvironment = _interp->newObject(/*prototype =*/ 0);
_idEnvironment = _interp->newObject(/*prototype =*/ 0);
_functionEnvironment = _interp->newObject(/*prototype =*/ 0);
_interestingObjectValue = 0;
_rootObjectValue = 0;
_qmlObjectDefinitions.clear();
_qmlObjectBindings.clear();
accept(program);
}
if (_interestingObjectValue) {
_functionEnvironment->setScope(_interestingObjectValue);
ObjectValue *Bind::scopeChainAt(Document::Ptr currentDocument, const Snapshot &snapshot,
Interpreter::Engine *interp, AST::UiObjectMember *currentObject)
{
Bind *currentBind = 0;
QList<Bind *> binds;
if (_interestingObjectValue != _rootObjectValue)
_interestingObjectValue->setScope(_rootObjectValue);
} else {
_functionEnvironment->setScope(_rootObjectValue);
Snapshot::const_iterator end = snapshot.end();
for (Snapshot::const_iterator iter = snapshot.begin(); iter != end; ++iter) {
Document::Ptr doc = *iter;
Bind *newBind = new Bind(doc, interp);
binds += newBind;
if (doc == currentDocument)
currentBind = newBind;
}
_idEnvironment->setScope(_functionEnvironment);
_typeEnvironment->setScope(_idEnvironment);
return _typeEnvironment;
LinkImports()(binds);
ObjectValue *scope = Link()(binds, currentBind, currentObject);
qDeleteAll(binds);
return scope;
}
void Bind::accept(Node *node)
......@@ -127,6 +135,7 @@ static QString serialize(UiQualifiedId *qualifiedId, QChar delimiter)
import "http://www.ovi.com/" as Ovi
*/
// ### TODO: Move to LinkImports
bool Bind::visit(UiImport *ast)
{
if (! (ast->importUri || ast->fileName))
......@@ -171,38 +180,6 @@ bool Bind::visit(UiImport *ast)
namespaceObject->setProperty(object->qmlTypeName(), object);
}
#endif // NO_DECLARATIVE_BACKEND
} else if (ast->fileName) {
// got an import "contents"
const QString relativePath = ast->fileName->asString();
const QList<Document::Ptr> userComponents = _snapshot.importedDocuments(_doc, relativePath);
foreach (Document::Ptr userComponent, userComponents) {
if (UiProgram *program = userComponent->qmlProgram()) {
if (UiObjectMemberList *members = program->members) {
if (UiObjectDefinition *def = cast<UiObjectDefinition *>(members->member)) {
const ObjectValue *prototype = lookupType(def->qualifiedTypeNameId);
ObjectValue *objectValue = _interp->newObject(prototype);
if (def->initializer) {
for (AST::UiObjectMemberList *it = def->initializer->members; it; it = it->next) {
if (AST::UiPublicMember *prop = AST::cast<AST::UiPublicMember *>(it->member)) {
if (prop->name && prop->memberType) {
const QString propName = prop->name->asString();
const QString propType = prop->memberType->asString();
objectValue->setProperty(propName, _interp->defaultValueForBuiltinType(propType));
}
}
}
}
const QString componentName = userComponent->componentName();
if (! componentName.isEmpty()) {
objectValue->setClassName(componentName);
namespaceObject->setProperty(componentName, objectValue);
}
}
}
}
}
}
return false;
......@@ -225,24 +202,6 @@ bool Bind::visit(UiSourceElement *)
return true;
}
const ObjectValue *Bind::lookupType(UiQualifiedId *qualifiedTypeNameId)
{
const ObjectValue *objectValue = _typeEnvironment;
for (UiQualifiedId *iter = qualifiedTypeNameId; objectValue && iter; iter = iter->next) {
if (! iter->name)
return 0;
const Value *value = objectValue->property(iter->name->asString());
if (!value)
return 0;
objectValue = value->asObjectValue();
}
return objectValue;
}
ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitializer *initializer)
{
ObjectValue *parentObjectValue;
......@@ -253,8 +212,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
// Script blocks all contribute to the same scope
parentObjectValue = switchObjectValue(_functionEnvironment);
} else { // normal component instance
const ObjectValue *prototype = lookupType(qualifiedTypeNameId);
ObjectValue *objectValue = _interp->newObject(prototype);
ObjectValue *objectValue = _interp->newObject(/*prototype =*/0);
parentObjectValue = switchObjectValue(objectValue);
if (parentObjectValue)
objectValue->setProperty("parent", parentObjectValue);
......@@ -270,9 +228,8 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
bool Bind::visit(UiObjectDefinition *ast)
{
ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer);
_qmlObjectDefinitions.insert(ast, value);
if (_interestingMember == ast)
_interestingObjectValue = value;
return false;
}
......@@ -280,11 +237,10 @@ bool Bind::visit(UiObjectBinding *ast)
{
// const QString name = serialize(ast->qualifiedId);
ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer);
_qmlObjectBindings.insert(ast, value);
// ### FIXME: we don't handle dot-properties correctly (i.e. font.size)
// _currentObjectValue->setProperty(name, value);
if (_interestingMember == ast)
_interestingObjectValue = value;
return false;
}
......
......@@ -34,15 +34,26 @@
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsinterpreter.h>
#include <QtCore/QHash>
namespace QmlJS {
class LinkImports;
class Link;
class QMLJS_EXPORT Bind: protected AST::Visitor
{
public:
Bind(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp);
Bind(Document::Ptr doc, Interpreter::Engine *interp);
virtual ~Bind();
Interpreter::ObjectValue* operator()(AST::UiObjectMember *member);
void operator()();
// ### 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:
void accept(AST::Node *node);
......@@ -148,17 +159,20 @@ protected:
private:
Document::Ptr _doc;
Snapshot _snapshot;
Interpreter::Engine *_interp;
AST::UiObjectMember *_interestingMember;
Interpreter::ObjectValue *_currentObjectValue;
Interpreter::ObjectValue *_typeEnvironment;
Interpreter::ObjectValue *_idEnvironment;
Interpreter::ObjectValue *_functionEnvironment;
Interpreter::ObjectValue *_interestingObjectValue;
Interpreter::ObjectValue *_rootObjectValue;
QHash<AST::UiObjectDefinition *, Interpreter::ObjectValue *> _qmlObjectDefinitions;
QHash<AST::UiObjectBinding *, Interpreter::ObjectValue *> _qmlObjectBindings;
friend class LinkImports;
friend class Link;
};
} // end of namespace Qml
......
#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;
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;
}
void LinkImports::linkImports(Bind *bind, const QList<Bind *> &binds)
{
// ### TODO: remove all properties from _typeEnv
Document::Ptr doc = bind->_doc;
if (! (doc->qmlProgram() && doc->qmlProgram()->imports))
return;
QFileInfo fileInfo(doc->fileName());
const QString absolutePath = fileInfo.absolutePath();
// implicit imports
foreach (Bind *otherBind, binds) {
if (otherBind == bind)
continue;
Document::Ptr otherDoc = otherBind->_doc;
QFileInfo otherFileInfo(otherDoc->fileName());
const QString otherAbsolutePath = otherFileInfo.absolutePath();
if (otherAbsolutePath.size() < absolutePath.size()
|| otherAbsolutePath.left(absolutePath.size()) != absolutePath)
continue;
// ### TODO: implicit directory access not implemented
if (otherAbsolutePath != absolutePath)
continue;
bind->_typeEnvironment->setProperty(componentName(otherFileInfo.fileName()), otherBind->_rootObjectValue);
}
// explicit imports, whether directories or files
for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) {
if (! (it->import && it->import->fileName))
continue;
QString path = absolutePath;
path += QLatin1Char('/');
path += it->import->fileName->asString();
path = QDir::cleanPath(path);
foreach (Bind *otherBind, binds) {
Document::Ptr otherDoc = otherBind->_doc;
QFileInfo otherFileInfo(otherDoc->fileName());
const QString otherAbsolutePath = otherFileInfo.absolutePath();
if (path != otherDoc->fileName() && path != otherAbsolutePath)
continue;
bool directoryImport = (path == otherAbsolutePath);
bool fileImport = (path == otherDoc->fileName());
if (!directoryImport && !fileImport)
continue;
ObjectValue *importInto = bind->_typeEnvironment;
if (directoryImport && it->import->importId) {
// ### TODO: set importInto to a namespace object value
}
QString targetName;
if (fileImport && it->import->importId) {
targetName = it->import->importId->asString();
} else {
targetName = componentName(otherFileInfo.fileName());
}
importInto->setProperty(targetName, otherBind->_rootObjectValue);
}
}
}
static const ObjectValue *lookupType(ObjectValue *env, UiQualifiedId *id)
{
const ObjectValue *objectValue = env;
for (UiQualifiedId *iter = id; objectValue && iter; iter = iter->next) {
if (! iter->name)
return 0;
const Value *value = objectValue->property(iter->name->asString());
if (!value)
return 0;
objectValue = value->asObjectValue();
}
return objectValue;
}
void LinkImports::operator()(const QList<Bind *> &binds)
{
foreach (Bind *bind, binds) {
// Populate the _typeEnvironment with imports.
linkImports(bind, binds);
// 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(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;
value->setPrototype(lookupType(bind->_typeEnvironment, key->qualifiedTypeNameId));
}
}
}
}
ObjectValue *Link::operator()(const QList<Bind *> &binds, Bind *currentBind, UiObjectMember *currentObject)
{
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 0;
if (!scopeObject)
return 0;
// 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;
}
#ifndef QMLJSLINK_H
#define QMLJSLINK_H
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/parser/qmljsastfwd_p.h>
#include <qmljs/parser/qmljsengine_p.h>
#include <QtCore/QList>
namespace QmlJS {
class Bind;
class LinkImports
{
public:
void operator()(const QList<Bind *> &binds);
private:
void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId* targetNamespace);
void linkImports(Bind *bind, const QList<Bind *> &binds);
};
class Link
{
public:
Interpreter::ObjectValue *operator()(const QList<Bind *> &binds, Bind *currentBind, AST::UiObjectMember *currentObject);
};
} // namespace QmlJS
#endif // QMLJSLINK_H
......@@ -34,6 +34,7 @@
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljslink.h>
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljsscanner.h>
#include <qmljs/qmljscheck.h>
......@@ -589,8 +590,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
Interpreter::ObjectValue *scope = interp.globalObject();
if (isQmlFile) {
scope = interp.newObject(/* prototype = */ 0);
AST::UiObjectMember *declaringMember = 0;
const int cursorPosition = editor->position();
......@@ -600,8 +599,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
}
}
Bind bind(qmlDocument, snapshot, &interp);
scope = bind(declaringMember);
scope = Bind::scopeChainAt(qmlDocument, snapshot, &interp, declaringMember);
}
// Search for the operator that triggered the completion.
......@@ -612,16 +610,6 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
(completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
// It's a global completion.
// Process the visible user defined components.
QHashIterator<QString, Document::Ptr> componentIt(userComponents);
while (componentIt.hasNext()) {
componentIt.next();
TextEditor::CompletionItem item(this);
item.text = componentIt.key();
item.icon = componentIcon;
m_completions.append(item);
}
EnumerateProperties enumerateProperties;
enumerateProperties.setGlobalCompletion(true);
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(scope, /* lookAtScope = */ true));
......
......@@ -190,8 +190,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
}
Interpreter::Engine interp;
Bind bind(qmlDocument, snapshot, &interp);
Interpreter::ObjectValue *scope = bind(declaringMember);
Interpreter::ObjectValue *scope = Bind::scopeChainAt(qmlDocument, snapshot, &interp, declaringMember);
Check check(&interp);
const Interpreter::Value *value = check(expression, scope);
QStringList baseClasses;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment