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:
Christian Kamm
2011-07-12 14:55:27 +02:00
parent ed1321a4f9
commit f87dc61986
22 changed files with 671 additions and 416 deletions

View File

@@ -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) {