forked from qt-creator/qt-creator
QmlJS: Split Context and ScopeChain.
Context is created by Link and has information about imports for all Documents in a Snapshot. ScopeChain represents how lookup is done at a specific place in a Document. Change-Id: I874102d57bbaf1a497fa3f27633bed6ee75dcf10 Reviewed-on: http://codereview.qt.nokia.com/1694 Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
@@ -35,15 +35,15 @@
|
||||
#include "qmljsbind.h"
|
||||
#include "qmljscontext.h"
|
||||
#include "qmljsevaluate.h"
|
||||
#include "qmljsscopechain.h"
|
||||
#include "parser/qmljsast_p.h"
|
||||
|
||||
using namespace QmlJS;
|
||||
using namespace QmlJS::Interpreter;
|
||||
using namespace QmlJS::AST;
|
||||
|
||||
ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc)
|
||||
: _doc(doc)
|
||||
, _context(context)
|
||||
ScopeBuilder::ScopeBuilder(ScopeChain *scopeChain)
|
||||
: _scopeChain(scopeChain)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -69,16 +69,17 @@ void ScopeBuilder::push(AST::Node *node)
|
||||
case Node::Kind_FunctionExpression:
|
||||
case Node::Kind_UiPublicMember:
|
||||
{
|
||||
ObjectValue *scope = _doc->bind()->findAttachedJSScope(node);
|
||||
if (scope)
|
||||
_context->scopeChain().jsScopes += scope;
|
||||
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node);
|
||||
if (scope) {
|
||||
QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes();
|
||||
jsScopes += scope;
|
||||
_scopeChain->setJsScopes(jsScopes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_context->scopeChain().update();
|
||||
}
|
||||
|
||||
void ScopeBuilder::push(const QList<AST::Node *> &nodes)
|
||||
@@ -99,9 +100,14 @@ void ScopeBuilder::pop()
|
||||
case Node::Kind_FunctionExpression:
|
||||
case Node::Kind_UiPublicMember:
|
||||
{
|
||||
ObjectValue *scope = _doc->bind()->findAttachedJSScope(toRemove);
|
||||
if (scope)
|
||||
_context->scopeChain().jsScopes.removeLast();
|
||||
ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(toRemove);
|
||||
if (scope) {
|
||||
QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes();
|
||||
if (!jsScopes.isEmpty()) {
|
||||
jsScopes.removeLast();
|
||||
_scopeChain->setJsScopes(jsScopes);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -112,103 +118,12 @@ void ScopeBuilder::pop()
|
||||
if (! _nodes.isEmpty()
|
||||
&& (cast<UiObjectDefinition *>(toRemove) || cast<UiObjectBinding *>(toRemove)))
|
||||
setQmlScopeObject(_nodes.last());
|
||||
|
||||
_context->scopeChain().update();
|
||||
}
|
||||
|
||||
void ScopeBuilder::initializeRootScope()
|
||||
{
|
||||
ScopeChain &scopeChain = _context->scopeChain();
|
||||
if (scopeChain.qmlComponentScope
|
||||
&& scopeChain.qmlComponentScope->document == _doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
scopeChain = ScopeChain(); // reset
|
||||
|
||||
Interpreter::ValueOwner *valueOwner = _context->valueOwner();
|
||||
|
||||
// ### TODO: This object ought to contain the global namespace additions by QML.
|
||||
scopeChain.globalScope = valueOwner->globalObject();
|
||||
|
||||
if (! _doc) {
|
||||
scopeChain.update();
|
||||
return;
|
||||
}
|
||||
|
||||
Bind *bind = _doc->bind();
|
||||
QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes;
|
||||
const Snapshot &snapshot = _context->snapshot();
|
||||
|
||||
ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain;
|
||||
scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain);
|
||||
if (_doc->qmlProgram()) {
|
||||
componentScopes.insert(_doc.data(), chain);
|
||||
makeComponentChain(_doc, snapshot, chain, &componentScopes);
|
||||
|
||||
if (const Imports *imports = _context->imports(_doc.data())) {
|
||||
scopeChain.qmlTypes = imports->typeScope();
|
||||
scopeChain.jsImports = imports->jsImportScope();
|
||||
}
|
||||
} else {
|
||||
// add scope chains for all components that import this file
|
||||
foreach (Document::Ptr otherDoc, snapshot) {
|
||||
foreach (const ImportInfo &import, otherDoc->bind()->imports()) {
|
||||
if (import.type() == ImportInfo::FileImport && _doc->fileName() == import.name()) {
|
||||
ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
|
||||
componentScopes.insert(otherDoc.data(), component);
|
||||
chain->instantiatingComponents += component;
|
||||
makeComponentChain(otherDoc, snapshot, component, &componentScopes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ### TODO: Which type environment do scripts see?
|
||||
|
||||
if (bind->rootObjectValue())
|
||||
scopeChain.jsScopes += bind->rootObjectValue();
|
||||
}
|
||||
|
||||
scopeChain.update();
|
||||
}
|
||||
|
||||
void ScopeBuilder::makeComponentChain(
|
||||
Document::Ptr doc,
|
||||
const Snapshot &snapshot,
|
||||
ScopeChain::QmlComponentChain *target,
|
||||
QHash<Document *, ScopeChain::QmlComponentChain *> *components)
|
||||
{
|
||||
if (!doc->qmlProgram())
|
||||
return;
|
||||
|
||||
Bind *bind = doc->bind();
|
||||
|
||||
// add scopes for all components instantiating this one
|
||||
foreach (Document::Ptr otherDoc, snapshot) {
|
||||
if (otherDoc == doc)
|
||||
continue;
|
||||
if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) {
|
||||
if (components->contains(otherDoc.data())) {
|
||||
// target->instantiatingComponents += components->value(otherDoc.data());
|
||||
} else {
|
||||
ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain;
|
||||
components->insert(otherDoc.data(), component);
|
||||
target->instantiatingComponents += component;
|
||||
|
||||
makeComponentChain(otherDoc, snapshot, component, components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build this component scope
|
||||
target->document = doc;
|
||||
}
|
||||
|
||||
void ScopeBuilder::setQmlScopeObject(Node *node)
|
||||
{
|
||||
ScopeChain &scopeChain = _context->scopeChain();
|
||||
|
||||
if (_doc->bind()->isGroupedPropertyBinding(node)) {
|
||||
QList<const ObjectValue *> qmlScopeObjects;
|
||||
if (_scopeChain->document()->bind()->isGroupedPropertyBinding(node)) {
|
||||
UiObjectDefinition *definition = cast<UiObjectDefinition *>(node);
|
||||
if (!definition)
|
||||
return;
|
||||
@@ -219,21 +134,21 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
scopeChain.qmlScopeObjects.clear();
|
||||
scopeChain.qmlScopeObjects += object;
|
||||
qmlScopeObjects += object;
|
||||
_scopeChain->setQmlScopeObjects(qmlScopeObjects);
|
||||
return;
|
||||
}
|
||||
|
||||
const ObjectValue *scopeObject = _doc->bind()->findQmlObject(node);
|
||||
const ObjectValue *scopeObject = _scopeChain->document()->bind()->findQmlObject(node);
|
||||
if (scopeObject) {
|
||||
scopeChain.qmlScopeObjects.clear();
|
||||
scopeChain.qmlScopeObjects += scopeObject;
|
||||
qmlScopeObjects += scopeObject;
|
||||
} else {
|
||||
return; // Probably syntax errors, where we're working with a "recovered" AST.
|
||||
}
|
||||
|
||||
// check if the object has a Qt.ListElement or Qt.Connections ancestor
|
||||
// ### allow only signal bindings for Connections
|
||||
PrototypeIterator iter(scopeObject, _context);
|
||||
PrototypeIterator iter(scopeObject, _scopeChain->context());
|
||||
iter.next();
|
||||
while (iter.hasNext()) {
|
||||
const ObjectValue *prototype = iter.next();
|
||||
@@ -242,15 +157,15 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
|
||||
|| qmlMetaObject->className() == QLatin1String("Connections")
|
||||
) && (qmlMetaObject->packageName() == QLatin1String("Qt")
|
||||
|| qmlMetaObject->packageName() == QLatin1String("QtQuick"))) {
|
||||
scopeChain.qmlScopeObjects.clear();
|
||||
qmlScopeObjects.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if the object has a Qt.PropertyChanges ancestor
|
||||
const ObjectValue *prototype = scopeObject->prototype(_context);
|
||||
prototype = isPropertyChangesObject(_context, prototype);
|
||||
const ObjectValue *prototype = scopeObject->prototype(_scopeChain->context());
|
||||
prototype = isPropertyChangesObject(_scopeChain->context(), prototype);
|
||||
// find the target script binding
|
||||
if (prototype) {
|
||||
UiObjectInitializer *initializer = 0;
|
||||
@@ -264,31 +179,33 @@ void ScopeBuilder::setQmlScopeObject(Node *node)
|
||||
if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name
|
||||
&& scriptBinding->qualifiedId->name->asString() == QLatin1String("target")
|
||||
&& ! scriptBinding->qualifiedId->next) {
|
||||
Evaluate evaluator(_context);
|
||||
Evaluate evaluator(_scopeChain);
|
||||
const Value *targetValue = evaluator(scriptBinding->statement);
|
||||
|
||||
if (const ObjectValue *target = value_cast<const ObjectValue *>(targetValue)) {
|
||||
scopeChain.qmlScopeObjects.prepend(target);
|
||||
qmlScopeObjects.prepend(target);
|
||||
} else {
|
||||
scopeChain.qmlScopeObjects.clear();
|
||||
qmlScopeObjects.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_scopeChain->setQmlScopeObjects(qmlScopeObjects);
|
||||
}
|
||||
|
||||
const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id)
|
||||
{
|
||||
// do a name lookup on the scope objects
|
||||
const Value *result = 0;
|
||||
foreach (const ObjectValue *scopeObject, _context->scopeChain().qmlScopeObjects) {
|
||||
foreach (const ObjectValue *scopeObject, _scopeChain->qmlScopeObjects()) {
|
||||
const ObjectValue *object = scopeObject;
|
||||
for (UiQualifiedId *it = id; it; it = it->next) {
|
||||
if (!it->name)
|
||||
return 0;
|
||||
result = object->lookupMember(it->name->asString(), _context);
|
||||
result = object->lookupMember(it->name->asString(), _scopeChain->context());
|
||||
if (!result)
|
||||
break;
|
||||
if (it->next) {
|
||||
|
||||
Reference in New Issue
Block a user