Commit 1f0b717a authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Completion for global variables in JS.

* Support for lookup JS vs Qml
* Bind global variables.

Done-with: ckamm
parent 4038e16d
......@@ -41,88 +41,6 @@ using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;
namespace {
class ASTObjectValue: public ObjectValue
{
UiQualifiedId *_typeName;
UiObjectInitializer *_initializer;
public:
ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Interpreter::Engine *engine)
: ObjectValue(engine), _typeName(typeName), _initializer(initializer)
{
}
virtual void processMembers(MemberProcessor *processor) const
{
if (_initializer) {
for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
if (def->name && def->memberType) {
const QString propName = def->name->asString();
const QString propType = def->memberType->asString();
processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType));
}
}
}
}
ObjectValue::processMembers(processor);
}
};
class ASTFunctionValue: public FunctionValue
{
FunctionDeclaration *_ast;
QList<NameId *> _argumentNames;
public:
ASTFunctionValue(FunctionDeclaration *ast, Interpreter::Engine *engine)
: FunctionValue(engine), _ast(ast)
{
setPrototype(engine->functionPrototype());
for (FormalParameterList *it = ast->formals; it; it = it->next)
_argumentNames.append(it->name);
}
FunctionDeclaration *ast() const { return _ast; }
virtual const Value *returnValue() const
{
return engine()->undefinedValue();
}
virtual int argumentCount() const
{
return _argumentNames.size();
}
virtual const Value *argument(int) const
{
return engine()->undefinedValue();
}
virtual QString argumentName(int index) const
{
if (index < _argumentNames.size()) {
if (NameId *nameId = _argumentNames.at(index))
return nameId->asString();
}
return FunctionValue::argumentName(index);
}
virtual bool isVariadic() const
{
return true;
}
};
} // end of anonymous namespace
Bind::Bind(Document *doc)
: _doc(doc),
_currentObjectValue(0),
......@@ -130,18 +48,8 @@ Bind::Bind(Document *doc)
_functionEnvironment(0),
_rootObjectValue(0)
{
if (!_doc)
return;
if (_doc->qmlProgram()) {
_idEnvironment = _interp.newObject(/*prototype =*/ 0);
_functionEnvironment = _interp.newObject(/*prototype =*/ 0);
} else if (_doc->jsProgram()) {
_currentObjectValue = _interp.globalObject();
_rootObjectValue = _interp.globalObject();
}
accept(_doc->ast());
if (_doc)
accept(_doc->ast());
}
Bind::~Bind()
......@@ -248,6 +156,20 @@ void Bind::accept(Node *node)
Node::accept(node, this);
}
bool Bind::visit(AST::UiProgram *)
{
_idEnvironment = _interp.newObject(/*prototype =*/ 0);
_functionEnvironment = _interp.newObject(/*prototype =*/ 0);
return true;
}
bool Bind::visit(AST::Program *)
{
_currentObjectValue = _interp.globalObject();
_rootObjectValue = _interp.globalObject();
return true;
}
bool Bind::visit(UiImport *ast)
{
if (ast->fileName) {
......@@ -305,6 +227,16 @@ bool Bind::visit(UiArrayBinding *)
return true;
}
bool Bind::visit(VariableDeclaration *ast)
{
if (! ast->name)
return false;
ASTVariableReference *ref = new ASTVariableReference(ast, &_interp);
_currentObjectValue->setProperty(ast->name->asString(), ref);
return false;
}
bool Bind::visit(FunctionDeclaration *ast)
{
if (!ast->name)
......
......@@ -61,6 +61,9 @@ protected:
void accept(AST::Node *node);
virtual bool visit(AST::UiProgram *ast);
virtual bool visit(AST::Program *ast);
// Ui
virtual bool visit(AST::UiImport *ast);
virtual bool visit(AST::UiPublicMember *ast);
......@@ -68,7 +71,10 @@ protected:
virtual bool visit(AST::UiObjectBinding *ast);
virtual bool visit(AST::UiScriptBinding *ast);
virtual bool visit(AST::UiArrayBinding *ast);
// QML/JS
virtual bool visit(AST::FunctionDeclaration *ast);
virtual bool visit(AST::VariableDeclaration *ast);
protected:
Interpreter::ObjectValue *switchObjectValue(Interpreter::ObjectValue *newObjectValue);
......
......@@ -37,9 +37,9 @@
using namespace QmlJS;
using namespace QmlJS::Interpreter;
Check::Check(Link *link)
: _engine(link->engine()),
_link(link),
Check::Check(Context *context)
: _engine(context->engine()),
_context(context),
_scope(_engine->globalObject()),
_result(0)
{
......@@ -66,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(_link->context());
result = ref->value(_context);
if (! result)
result = _engine->undefinedValue();
......@@ -165,7 +165,7 @@ bool Check::visit(AST::UiQualifiedId *ast)
if (! ast->name)
return false;
const Value *value = _link->lookup(ast->name->asString());
const Value *value = _context->lookup(ast->name->asString());
if (! ast->next) {
_result = value;
......@@ -213,7 +213,7 @@ bool Check::visit(AST::IdentifierExpression *ast)
if (! ast->name)
return false;
_result = _link->lookup(ast->name->asString());
_result = _context->lookup(ast->name->asString());
return false;
}
......
......@@ -48,7 +48,7 @@ namespace Interpreter {
class QMLJS_EXPORT Check: protected AST::Visitor
{
public:
Check(Link *link);
Check(Interpreter::Context *context);
virtual ~Check();
const Interpreter::Value *operator()(AST::Node *ast);
......@@ -158,7 +158,7 @@ protected:
private:
QmlJS::Document::Ptr _doc;
Interpreter::Engine *_engine;
Link *_link;
Interpreter::Context *_context;
const Interpreter::ObjectValue *_scope;
const Interpreter::Value *_result;
};
......
......@@ -28,7 +28,8 @@
**************************************************************************/
#include "qmljsinterpreter.h"
#include "qmljscheck.h"
#include "parser/qmljsast_p.h"
#include <QtCore/QMetaObject>
#include <QtCore/QMetaProperty>
#include <QtCore/QDebug>
......@@ -42,6 +43,7 @@
#endif
using namespace QmlJS::Interpreter;
using namespace QmlJS::AST;
namespace {
......@@ -677,7 +679,8 @@ void StringValue::accept(ValueVisitor *visitor) const
Context::Context(Engine *engine)
: _engine(engine)
: _engine(engine),
_lookupMode(JSLookup)
{
}
......@@ -690,6 +693,46 @@ Engine *Context::engine() const
return _engine;
}
Context::ScopeChain Context::scopeChain() const
{
return _scopeChain;
}
Context::LookupMode Context::lookupMode() const
{
return _lookupMode;
}
void Context::setLookupMode(LookupMode lookupMode)
{
_lookupMode = lookupMode;
}
void Context::pushScope(const ObjectValue *object)
{
_scopeChain.append(object);
}
void Context::popScope()
{
_scopeChain.removeLast();
}
const Value *Context::lookup(const QString &name) const
{
for (int index = _scopeChain.size() - 1; index != -1; --index) {
const ObjectValue *scope = _scopeChain.at(index);
if (const Value *member = scope->lookupMember(name)) {
if (_lookupMode == JSLookup || ! dynamic_cast<const ASTVariableReference *>(member)) {
return member;
}
}
}
return _engine->undefinedValue();
}
const Value *Context::property(const ObjectValue *object, const QString &name) const
{
const Properties properties = _properties.value(object);
......@@ -701,14 +744,21 @@ void Context::setProperty(const ObjectValue *object, const QString &name, const
_properties[object].insert(name, value);
}
Reference::Reference()
Reference::Reference(Engine *engine)
: _engine(engine)
{
_engine->registerValue(this);
}
Reference::~Reference()
{
}
Engine *Reference::engine() const
{
return _engine;
}
const Reference *Reference::asReference() const
{
return this;
......@@ -719,6 +769,10 @@ void Reference::accept(ValueVisitor *visitor) const
visitor->visit(this);
}
const Value *Reference::value(Context *) const
{
return _engine->undefinedValue();
}
MemberProcessor::MemberProcessor()
{
......@@ -757,7 +811,7 @@ ObjectValue::ObjectValue(Engine *engine)
: _engine(engine),
_prototype(0)
{
engine->registerObject(this);
engine->registerValue(this);
}
ObjectValue::~ObjectValue()
......@@ -1314,7 +1368,7 @@ Engine::Engine()
Engine::~Engine()
{
qDeleteAll(_registeredObjects);
qDeleteAll(_registeredValues);
}
const NullValue *Engine::nullValue() const
......@@ -1456,9 +1510,9 @@ const ObjectValue *Engine::mathObject() const
return _mathObject;
}
void Engine::registerObject(ObjectValue *object)
void Engine::registerValue(Value *value)
{
_registeredObjects.append(object);
_registeredValues.append(value);
}
const Value *Engine::convertToBoolean(const Value *value)
......@@ -1802,3 +1856,94 @@ QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix,
}
#endif
ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, Engine *engine)
: ObjectValue(engine), _typeName(typeName), _initializer(initializer)
{
}
ASTObjectValue::~ASTObjectValue()
{
}
void ASTObjectValue::processMembers(MemberProcessor *processor) const
{
if (_initializer) {
for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
if (def->name && def->memberType) {
const QString propName = def->name->asString();
const QString propType = def->memberType->asString();
processor->processProperty(propName, engine()->defaultValueForBuiltinType(propType));
}
}
}
}
ObjectValue::processMembers(processor);
}
ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, Engine *engine)
: Reference(engine), _ast(ast)
{
}
ASTVariableReference::~ASTVariableReference()
{
}
const Value *ASTVariableReference::value(Context *context) const
{
Check check(context);
return check(_ast->expression);
}
ASTFunctionValue::ASTFunctionValue(FunctionDeclaration *ast, Engine *engine)
: FunctionValue(engine), _ast(ast)
{
setPrototype(engine->functionPrototype());
for (FormalParameterList *it = ast->formals; it; it = it->next)
_argumentNames.append(it->name);
}
ASTFunctionValue::~ASTFunctionValue()
{
}
FunctionDeclaration *ASTFunctionValue::ast() const
{
return _ast;
}
const Value *ASTFunctionValue::returnValue() const
{
return engine()->undefinedValue();
}
int ASTFunctionValue::argumentCount() const
{
return _argumentNames.size();
}
const Value *ASTFunctionValue::argument(int) const
{
return engine()->undefinedValue();
}
QString ASTFunctionValue::argumentName(int index) const
{
if (index < _argumentNames.size()) {
if (NameId *nameId = _argumentNames.at(index))
return nameId->asString();
}
return FunctionValue::argumentName(index);
}
bool ASTFunctionValue::isVariadic() const
{
return true;
}
......@@ -32,6 +32,7 @@
#include <qmljs/qmljs_global.h>
#include <qmljs/qmljsmetatypesystem.h>
#include <qmljs/parser/qmljsastfwd_p.h>
#include <QtCore/QList>
#include <QtCore/QString>
......@@ -39,6 +40,9 @@
#include <QtCore/QSet>
namespace QmlJS {
class NameId;
namespace Interpreter {
////////////////////////////////////////////////////////////////////////////////
......@@ -207,11 +211,28 @@ public:
class QMLJS_EXPORT Context
{
public:
typedef QList<const ObjectValue *> ScopeChain;
enum LookupMode {
JSLookup,
QmlLookup
};
public:
Context(Engine *engine);
virtual ~Context();
~Context();
Engine *engine() const;
ScopeChain scopeChain() const;
LookupMode lookupMode() const;
void setLookupMode(LookupMode lookupMode);
void pushScope(const ObjectValue *object);
void popScope();
const Value *lookup(const QString &name) const;
const Value *property(const ObjectValue *object, const QString &name) const;
void setProperty(const ObjectValue *object, const QString &name, const Value *value);
......@@ -220,20 +241,26 @@ private:
typedef QHash<QString, const Value *> Properties;
Engine *_engine;
LookupMode _lookupMode;
QHash<const ObjectValue *, Properties> _properties;
ScopeChain _scopeChain;
};
class QMLJS_EXPORT Reference: public Value
{
public:
Reference();
Reference(Engine *engine);
virtual ~Reference();
virtual const Value *value(const Context *context) const = 0;
Engine *engine() const;
virtual const Value *value(Context *context) const;
// Value interface
virtual const Reference *asReference() const;
virtual void accept(ValueVisitor *) const;
private:
Engine *_engine;
};
class QMLJS_EXPORT ObjectValue: public Value
......@@ -506,8 +533,6 @@ public:
ObjectValue *globalObject() const;
const ObjectValue *mathObject() const;
void registerObject(ObjectValue *object);
// prototypes
ObjectValue *objectPrototype() const;
ObjectValue *functionPrototype() const;
......@@ -539,6 +564,8 @@ public:
const MetaTypeSystem &metaTypeSystem() const
{ return _metaTypeSystem; }
void registerValue(Value *value); // internal
private:
void initializePrototypes();
......@@ -575,7 +602,7 @@ private:
NumberValue _numberValue;
BooleanValue _booleanValue;
StringValue _stringValue;
QList<ObjectValue *> _registeredObjects;
QList<Value *> _registeredValues;
ConvertToNumber _convertToNumber;
ConvertToString _convertToString;
......@@ -585,6 +612,51 @@ private:
MetaTypeSystem _metaTypeSystem;
};
// internal
class QMLJS_EXPORT ASTObjectValue: public ObjectValue
{
AST::UiQualifiedId *_typeName;
AST::UiObjectInitializer *_initializer;
public:
ASTObjectValue(AST::UiQualifiedId *typeName, AST::UiObjectInitializer *initializer, Engine *engine);
virtual ~ASTObjectValue();
virtual void processMembers(MemberProcessor *processor) const;
};
class QMLJS_EXPORT ASTVariableReference: public Reference
{
AST::VariableDeclaration *_ast;
public:
ASTVariableReference(AST::VariableDeclaration *ast, Engine *engine);
virtual ~ASTVariableReference();
virtual const Value *value(Context *context) const;
};
class QMLJS_EXPORT ASTFunctionValue: public FunctionValue
{
AST::FunctionDeclaration *_ast;
QList<NameId *> _argumentNames;
public:
ASTFunctionValue(AST::FunctionDeclaration *ast, Engine *engine);
virtual ~ASTFunctionValue();
AST::FunctionDeclaration *ast() const;
virtual const Value *returnValue() const;
virtual int argumentCount() const;
virtual const Value *argument(int) const;
virtual QString argumentName(int index) const;
virtual bool isVariadic() const;
};
} } // end of namespace QmlJS::Interpreter
#endif // QMLJS_INTERPRETER_H
......@@ -14,7 +14,6 @@ using namespace QmlJS::AST;
Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engine *interp)
: _snapshot(snapshot)
, _interp(interp)
, _context(interp)
{
_docs = reachableDocuments(currentDoc, snapshot);
......@@ -41,64 +40,58 @@ Context *Link::context()
return &_context;
}