forked from qt-creator/qt-creator
Separate bind into bind, link imports and building the scope chain.
This commit is contained in:
@@ -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
|
||||
|
||||
174
src/libs/qmljs/qmljslink.cpp
Normal file
174
src/libs/qmljs/qmljslink.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#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;
|
||||
}
|
||||
31
src/libs/qmljs/qmljslink.h
Normal file
31
src/libs/qmljs/qmljslink.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user