Files
qt-creator/src/libs/qmljs/qmljsbind.cpp

344 lines
10 KiB
C++
Raw Normal View History

/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "parser/qmljsast_p.h"
#include "qmljsbind.h"
#include "qmljslink.h"
#include "qmljsmetatypesystem.h"
#include <QtCore/QDebug>
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;
2010-02-01 12:43:36 +01:00
namespace {
2010-02-01 13:54:44 +01:00
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);
}
};
2010-02-01 12:43:36 +01:00
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::Ptr doc, Interpreter::Engine *interp)
2010-01-26 16:21:03 +01:00
: _doc(doc),
_interp(interp),
2010-01-26 15:51:31 +01:00
_currentObjectValue(0),
_typeEnvironment(0),
_idEnvironment(0),
_functionEnvironment(0),
2010-01-26 15:51:31 +01:00
_rootObjectValue(0)
{
2010-02-01 12:26:40 +01:00
if (_doc && _doc->qmlProgram() != 0) {
UiProgram *program = _doc->qmlProgram();
_currentObjectValue = 0;
_typeEnvironment = _interp->newObject(/*prototype =*/ 0);
_idEnvironment = _interp->newObject(/*prototype =*/ 0);
_functionEnvironment = _interp->newObject(/*prototype =*/ 0);
_rootObjectValue = 0;
_qmlObjectDefinitions.clear();
_qmlObjectBindings.clear();
accept(program);
}
}
Bind::~Bind()
{
}
2010-02-01 12:26:40 +01:00
ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue)
{
2010-02-01 12:26:40 +01:00
ObjectValue *oldObjectValue = _currentObjectValue;
_currentObjectValue = newObjectValue;
return oldObjectValue;
}
ObjectValue *Bind::scopeChainAt(Document::Ptr currentDocument, const Snapshot &snapshot,
Interpreter::Engine *interp, AST::UiObjectMember *currentObject)
{
Bind *currentBind = 0;
QList<Bind *> binds;
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;
}
2010-02-01 12:26:40 +01:00
LinkImports linkImports;
Link link;
linkImports(binds);
2010-01-28 16:29:45 +01:00
ObjectValue *scope = interp->globalObject();
if (currentBind)
2010-02-01 12:26:40 +01:00
scope = link(binds, currentBind, currentObject);
qDeleteAll(binds);
return scope;
}
2010-02-01 12:17:39 +01:00
QString Bind::toString(UiQualifiedId *qualifiedId, QChar delimiter)
{
QString result;
for (UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
if (iter != qualifiedId)
result += delimiter;
if (iter->name)
result += iter->name->asString();
}
return result;
}
2010-02-01 12:26:40 +01:00
ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitializer *initializer)
{
2010-02-01 12:26:40 +01:00
ObjectValue *parentObjectValue;
2010-02-01 12:26:40 +01:00
if (toString(qualifiedTypeNameId) == QLatin1String("Script")) {
// Script blocks all contribute to the same scope
parentObjectValue = switchObjectValue(_functionEnvironment);
} else { // normal component instance
2010-02-01 13:54:44 +01:00
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, _interp);
2010-02-01 12:26:40 +01:00
parentObjectValue = switchObjectValue(objectValue);
if (parentObjectValue)
objectValue->setProperty("parent", parentObjectValue);
else
_rootObjectValue = objectValue;
}
accept(initializer);
return switchObjectValue(parentObjectValue);
}
2010-02-01 12:26:40 +01:00
void Bind::accept(Node *node)
{
2010-02-01 12:26:40 +01:00
Node::accept(node, this);
}
/*
import Qt 4.6
import Qt 4.6 as Xxx
(import com.nokia.qt is the same as the ones above)
import "content"
import "content" as Xxx
import "content" 4.6
import "content" 4.6 as Xxx
import "http://www.ovi.com/" as Ovi
*/
// ### TODO: Move to LinkImports
bool Bind::visit(UiImport *ast)
{
if (! (ast->importUri || ast->fileName))
return false; // nothing to do.
ObjectValue *namespaceObject = 0;
if (ast->asToken.isValid()) { // with namespace we insert an object in the type env. to hold the imported types
if (!ast->importId)
return false; // this should never happen, but better be safe than sorry
2010-01-26 16:21:03 +01:00
namespaceObject = _interp->newObject(/*prototype */ 0);
_typeEnvironment->setProperty(ast->importId->asString(), namespaceObject);
2010-01-26 16:21:03 +01:00
} else { // without namespace we insert all types directly into the type env.
namespaceObject = _typeEnvironment;
}
// look at files first
// else try the metaobject system
if (ast->importUri) {
2010-02-01 12:17:39 +01:00
const QString package = toString(ast->importUri, '/');
int majorVersion = -1; // ### TODO: Check these magic version numbers
int minorVersion = -1; // ### TODO: Check these magic version numbers
if (ast->versionToken.isValid()) {
const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length);
const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1) {
// only major (which is probably invalid, but let's handle it anyway)
majorVersion = versionString.toInt();
minorVersion = 0; // ### TODO: Check with magic version numbers above
} else {
majorVersion = versionString.left(dotIdx).toInt();
minorVersion = versionString.mid(dotIdx + 1).toInt();
}
}
#ifndef NO_DECLARATIVE_BACKEND
foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) {
namespaceObject->setProperty(object->qmlTypeName(), object);
}
#endif // NO_DECLARATIVE_BACKEND
}
return false;
}
2010-02-01 16:40:42 +01:00
bool Bind::visit(UiPublicMember *)
{
2010-02-01 16:40:42 +01:00
// nothing to do.
return false;
}
bool Bind::visit(UiObjectDefinition *ast)
{
ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer);
_qmlObjectDefinitions.insert(ast, value);
return false;
}
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);
return false;
}
bool Bind::visit(UiScriptBinding *ast)
{
2010-02-01 12:17:39 +01:00
if (toString(ast->qualifiedId) == QLatin1String("id")) {
if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement))
if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression))
if (i->name)
_idEnvironment->setProperty(i->name->asString(), _currentObjectValue);
2010-02-01 12:17:39 +01:00
}
return false;
}
bool Bind::visit(UiArrayBinding *)
{
// ### FIXME: do we need to store the members into the property? Or, maybe the property type is an JS Array?
return true;
}
bool Bind::visit(FunctionDeclaration *ast)
{
if (!ast->name)
return false;
// the first declaration counts
if (_currentObjectValue->property(ast->name->asString()))
return false;
2010-02-01 12:43:36 +01:00
ASTFunctionValue *function = new ASTFunctionValue(ast, _interp);
_currentObjectValue->setProperty(ast->name->asString(), function);
return false; // ### eventually want to visit function bodies
}