Commit 49c43aaa authored by Christian Kamm's avatar Christian Kamm
Browse files

Get rid of Environment, introduce external ScopeChain in Link.

Done-with: Roberto
parent ba18e700
......@@ -29,6 +29,7 @@
#include "qmljscheck.h"
#include "qmljsinterpreter.h"
#include "qmljslink.h"
#include "parser/qmljsparser_p.h"
#include "parser/qmljsast_p.h"
#include <QtCore/QDebug>
......@@ -36,10 +37,10 @@
using namespace QmlJS;
using namespace QmlJS::Interpreter;
Check::Check(Interpreter::Engine *engine, Interpreter::Context *context)
: _engine(engine),
_context(context),
_scope(engine->globalObject()),
Check::Check(Link *link)
: _engine(link->engine()),
_link(link),
_scope(_engine->globalObject()),
_result(0)
{
}
......@@ -48,12 +49,9 @@ Check::~Check()
{
}
const Interpreter::Value *Check::operator()(AST::Node *ast, const Interpreter::ObjectValue *scope)
const Interpreter::Value *Check::operator()(AST::Node *ast)
{
const Interpreter::ObjectValue *previousScope = switchScope(scope);
const Interpreter::Value *result = check(ast);
(void) switchScope(previousScope);
return result;
return check(ast);
}
const Interpreter::Value *Check::check(AST::Node *ast)
......@@ -68,7 +66,7 @@ const Interpreter::Value *Check::check(AST::Node *ast)
const Value *result = switchResult(previousResult);
if (const Reference *ref = value_cast<const Reference *>(result))
result = ref->value(_context);
result = ref->value(_link->context());
if (! result)
result = _engine->undefinedValue();
......@@ -167,7 +165,7 @@ bool Check::visit(AST::UiQualifiedId *ast)
if (! ast->name)
return false;
const Value *value = _scope->lookup(ast->name->asString());
const Value *value = _link->lookup(ast->name->asString());
if (! ast->next) {
_result = value;
......@@ -215,7 +213,7 @@ bool Check::visit(AST::IdentifierExpression *ast)
if (! ast->name)
return false;
_result = _scope->lookup(ast->name->asString());
_result = _link->lookup(ast->name->asString());
return false;
}
......
......@@ -35,6 +35,8 @@
namespace QmlJS {
class Link;
namespace Interpreter {
class Engine;
class Context;
......@@ -46,10 +48,10 @@ namespace Interpreter {
class QMLJS_EXPORT Check: protected AST::Visitor
{
public:
Check(Interpreter::Engine *engine, Interpreter::Context *context);
Check(Link *link);
virtual ~Check();
const Interpreter::Value *operator()(AST::Node *ast, const Interpreter::ObjectValue *scope);
const Interpreter::Value *operator()(AST::Node *ast);
protected:
void accept(AST::Node *node);
......@@ -156,7 +158,7 @@ protected:
private:
QmlJS::Document::Ptr _doc;
Interpreter::Engine *_engine;
Interpreter::Context *_context;
Link *_link;
const Interpreter::ObjectValue *_scope;
const Interpreter::Value *_result;
};
......
......@@ -622,39 +622,6 @@ const Reference *Value::asReference() const
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Environment
////////////////////////////////////////////////////////////////////////////////
Environment::Environment()
{
}
Environment::~Environment()
{
}
const Environment *Environment::parent() const
{
return 0;
}
const Value *Environment::lookup(const QString &name) const
{
if (const Value *member = lookupMember(name))
return member;
else if (const Environment *p = parent())
return p->lookup(name);
else
return 0;
}
const Value *Environment::lookupMember(const QString &) const
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Values
////////////////////////////////////////////////////////////////////////////////
......@@ -788,8 +755,7 @@ bool MemberProcessor::processGeneratedSlot(const QString &, const Value *)
ObjectValue::ObjectValue(Engine *engine)
: _engine(engine),
_prototype(0),
_scope(0)
_prototype(0)
{
engine->registerObject(this);
}
......@@ -818,16 +784,6 @@ const ObjectValue *ObjectValue::prototype() const
return _prototype;
}
const ObjectValue *ObjectValue::scope() const
{
return _scope;
}
void ObjectValue::setScope(const ObjectValue *scope)
{
_scope = scope;
}
void ObjectValue::setProperty(const QString &name, const Value *value)
{
_members[name] = value;
......@@ -893,11 +849,6 @@ void ObjectValue::processMembers(MemberProcessor *processor) const
}
}
const Environment *ObjectValue::parent() const
{
return _scope;
}
const Value *ObjectValue::lookupMember(const QString &name) const
{
if (const Value *m = _members.value(name))
......
......@@ -150,20 +150,6 @@ template <> Q_INLINE_TEMPLATE const Reference *value_cast(const Value *v)
else return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Execution environment
////////////////////////////////////////////////////////////////////////////////
class QMLJS_EXPORT Environment
{
public:
Environment();
virtual ~Environment();
virtual const Environment *parent() const;
virtual const Value *lookup(const QString &name) const;
virtual const Value *lookupMember(const QString &name) const;
};
////////////////////////////////////////////////////////////////////////////////
// Value nodes
////////////////////////////////////////////////////////////////////////////////
......@@ -250,7 +236,7 @@ public:
virtual void accept(ValueVisitor *) const;
};
class QMLJS_EXPORT ObjectValue: public Value, public Environment
class QMLJS_EXPORT ObjectValue: public Value
{
public:
ObjectValue(Engine *engine);
......@@ -264,17 +250,12 @@ public:
const ObjectValue *prototype() const;
void setPrototype(const ObjectValue *prototype);
const ObjectValue *scope() const;
void setScope(const ObjectValue *scope);
virtual void processMembers(MemberProcessor *processor) const;
virtual const Value *property(const QString &name) const;
virtual void setProperty(const QString &name, const Value *value);
virtual void removeProperty(const QString &name);
// Environment interface
virtual const Environment *parent() const;
virtual const Value *lookupMember(const QString &name) const;
// Value interface
......@@ -287,7 +268,6 @@ private:
private:
Engine *_engine;
const ObjectValue *_prototype;
const ObjectValue *_scope;
QHash<QString, const Value *> _members;
QString _className;
};
......
......@@ -24,69 +24,81 @@ Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engi
Link::~Link()
{
// unset all prototypes and scopes
// unset all prototypes
foreach (Document::Ptr doc, _docs) {
BindPtr bind = doc->bind();
if (doc->qmlProgram()) {
bind->_idEnvironment->setScope(0);
bind->_functionEnvironment->setScope(0);
foreach (ObjectValue *object, bind->_qmlObjects) {
object->setPrototype(0);
object->setScope(0);
}
} else if (doc->jsProgram()) {
bind->_rootObjectValue->setScope(0);
}
}
}
static ObjectValue *pushScope(ObjectValue *next, ObjectValue *onto)
Context *Link::context()
{
onto->setScope(next);
return next;
return &_context;
}
Context *Link::context()
Link::ScopeChain Link::scopeChain() const
{
return &_context;
return _scopeChain;
}
ObjectValue *Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
Interpreter::Engine *Link::engine()
{
BindPtr bind = doc->bind();
return _interp;
}
ObjectValue *scopeObject = 0;
if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
scopeObject = bind->_qmlObjects.value(definition);
void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
{
_scopeChain.clear();
if (! doc) {
_scopeChain.append(_interp->globalObject());
return;
}
if (!scopeObject)
return bind->_interp.globalObject();
BindPtr bind = doc->bind();
// Build the scope chain.
ObjectValue *scopeStart = _typeEnvironments.value(doc.data());
ObjectValue *scope = scopeStart;
scope = pushScope(bind->_idEnvironment, scope);
scope = pushScope(bind->_functionEnvironment, scope);
_scopeChain.append(_typeEnvironments.value(doc.data()));
_scopeChain.append(bind->_idEnvironment);
_scopeChain.append(bind->_functionEnvironment);
foreach (const QString &scriptFile, doc->bind()->includedScripts()) {
if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
if (scriptDoc->jsProgram()) {
scope = pushScope(scriptDoc->bind()->_rootObjectValue, scope);
_scopeChain.append(scriptDoc->bind()->_rootObjectValue);
}
}
}
scope = pushScope(scopeObject, scope);
if (scopeObject != bind->_rootObjectValue)
scope = pushScope(bind->_rootObjectValue, scope);
scope = pushScope(bind->_interp.globalObject(), scope);
if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject)) {
ObjectValue *scopeObject = bind->_qmlObjects.value(definition);
_scopeChain.append(scopeObject);
// ### FIXME: should add the root regardless
if (scopeObject != bind->_rootObjectValue)
_scopeChain.append(bind->_rootObjectValue);
}
_scopeChain.append(bind->_interp.globalObject());
// May want to link to instantiating components from here.
}
const Value *Link::lookup(const QString &name) const
{
foreach (const ObjectValue *scope, _scopeChain) {
if (const Value *member = scope->lookupMember(name)) {
return member;
}
}
return scopeStart;
return _interp->undefinedValue();
}
void Link::linkImports()
......
......@@ -18,15 +18,22 @@ namespace QmlJS {
*/
class QMLJS_EXPORT Link
{
public:
typedef QList<const Interpreter::ObjectValue *> ScopeChain;
public:
// Link all documents in snapshot reachable from doc.
Link(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp);
~Link();
Interpreter::Context *context();
ScopeChain scopeChain() const;
Interpreter::Engine *engine();
// Get the scope chain for the currentObject inside doc.
Interpreter::ObjectValue *scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
void scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
const Interpreter::Value *lookup(const QString &name) const;
private:
static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
......@@ -48,6 +55,7 @@ private:
Interpreter::Context _context;
QList<Document::Ptr> _docs;
QHash<Document *, Interpreter::ObjectValue *> _typeEnvironments;
ScopeChain _scopeChain;
};
} // namespace QmlJS
......
......@@ -129,10 +129,12 @@ class EnumerateProperties: private Interpreter::MemberProcessor
QSet<const Interpreter::ObjectValue *> _processed;
QHash<QString, const Interpreter::Value *> _properties;
bool _globalCompletion;
Link *_link;
public:
EnumerateProperties()
: _globalCompletion(false)
EnumerateProperties(Link *link)
: _globalCompletion(false),
_link(link)
{
}
......@@ -141,12 +143,16 @@ public:
_globalCompletion = globalCompletion;
}
QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value,
bool lookAtScope = false)
QHash<QString, const Interpreter::Value *> operator ()(bool lookAtScope = false)
{
_processed.clear();
_properties.clear();
enumerateProperties(value, lookAtScope);
if (!lookAtScope) {
enumerateProperties(_link->scopeChain().first());
} else {
foreach (const Interpreter::ObjectValue *scope, _link->scopeChain())
enumerateProperties(scope);
}
return _properties;
}
......@@ -183,25 +189,22 @@ private:
return true;
}
void enumerateProperties(const Interpreter::Value *value, bool lookAtScope)
void enumerateProperties(const Interpreter::Value *value)
{
if (! value)
return;
else if (const Interpreter::ObjectValue *object = value->asObjectValue()) {
enumerateProperties(object, lookAtScope);
enumerateProperties(object);
}
}
void enumerateProperties(const Interpreter::ObjectValue *object, bool lookAtScope)
void enumerateProperties(const Interpreter::ObjectValue *object)
{
if (! object || _processed.contains(object))
return;
_processed.insert(object);
enumerateProperties(object->prototype(), /* lookAtScope = */ false);
if (lookAtScope)
enumerateProperties(object->scope(), /* lookAtScope = */ true);
enumerateProperties(object->prototype());
object->processMembers(this);
}
......@@ -631,13 +634,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
Interpreter::Engine interp;
// Set up the current scope chain.
Interpreter::ObjectValue *scope = interp.globalObject();
Link link(document, snapshot, &interp);
if (document) {
AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
scope = link.scopeChainAt(document, declaringMember);
}
AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
link.scopeChainAt(document, declaringMember);
Link::ScopeChain scope = link.scopeChain();
// Search for the operator that triggered the completion.
QChar completionOperator;
......@@ -647,9 +648,9 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
(completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
// It's a global completion.
EnumerateProperties enumerateProperties;
EnumerateProperties enumerateProperties(&link);
enumerateProperties.setGlobalCompletion(true);
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(scope, /* lookAtScope = */ true));
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(/* lookAtScope = */ true));
while (it.hasNext()) {
it.next();
......@@ -670,15 +671,15 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
//qDebug() << "expression:" << expression;
if (expression != 0) {
Check evaluate(&interp, link.context());
Check evaluate(&link);
// Evaluate the expression under cursor.
const Interpreter::Value *value = interp.convertToObject(evaluate(expression , scope));
const Interpreter::Value *value = interp.convertToObject(evaluate(expression));
//qDebug() << "type:" << interp.typeId(value);
if (value && completionOperator == QLatin1Char('.')) { // member completion
EnumerateProperties enumerateProperties;
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
EnumerateProperties enumerateProperties(&link);
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties());
while (it.hasNext()) {
it.next();
......
......@@ -173,10 +173,10 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
Interpreter::Engine interp;
Link link(qmlDocument, snapshot, &interp);
const Interpreter::ObjectValue *scope = link.scopeChainAt(qmlDocument, declaringMember);
link.scopeChainAt(qmlDocument, declaringMember);
Check check(&interp, link.context());
const Interpreter::Value *value = check(node, scope);
Check check(&link);
const Interpreter::Value *value = check(node);
QStringList baseClasses;
m_toolTip = prettyPrint(value, &interp, &baseClasses);
......
Markdown is supported
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