-
Christian Kamm authored
* Don't build all default values (including the global object) separately for each ValueOwner instance. * Instead, keep all global, immutable values in a single, shared instance. While refactoring, some cases where we *modified* the global object had to be removed: * C++ context properties no longer get injected into the global object, instead they now have their own scope just above the global one. * The Qt object's prototype no longer gets modified in Link. Instead, it's now a reference to the "Qt" object provided in a qmltypes file. * The whole concept of a function 'Activation' that could potentially affect the global object was removed. Change-Id: Id382faf965efa747fcc7a9b0bc2c90429d84d61b Reviewed-by:
Leandro Melo <leandro.melo@nokia.com>
Christian Kamm authored* Don't build all default values (including the global object) separately for each ValueOwner instance. * Instead, keep all global, immutable values in a single, shared instance. While refactoring, some cases where we *modified* the global object had to be removed: * C++ context properties no longer get injected into the global object, instead they now have their own scope just above the global one. * The Qt object's prototype no longer gets modified in Link. Instead, it's now a reference to the "Qt" object provided in a qmltypes file. * The whole concept of a function 'Activation' that could potentially affect the global object was removed. Change-Id: Id382faf965efa747fcc7a9b0bc2c90429d84d61b Reviewed-by:
Leandro Melo <leandro.melo@nokia.com>
qmljsinterpreter.cpp 61.54 KiB
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "qmljslink.h"
#include "qmljsbind.h"
#include "qmljsscopebuilder.h"
#include "qmljsscopechain.h"
#include "qmljsscopeastpath.h"
#include "qmljstypedescriptionreader.h"
#include "qmljsvalueowner.h"
#include "qmljscontext.h"
#include "parser/qmljsast_p.h"
#include <languageutils/fakemetaobject.h>
#include <utils/qtcassert.h>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QMetaObject>
#include <QtCore/QMetaProperty>
#include <QtCore/QXmlStreamReader>
#include <QtCore/QProcess>
#include <QtCore/QDebug>
#include <algorithm>
using namespace LanguageUtils;
using namespace QmlJS;
using namespace QmlJS::AST;
/*!
\class QmlJS::Value
\brief Abstract base class for the result of a JS expression.
\sa Evaluate ValueOwner ValueVisitor
A Value represents a category of JavaScript values, such as number
(NumberValue), string (StringValue) or functions with a
specific signature (FunctionValue). It can also represent internal
categories such as "a QML component instantiation defined in a file"
(ASTObjectValue), "a QML component defined in C++"
(CppComponentValue) or "no specific information is available"
(UnknownValue).
The Value class itself provides accept() for admitting
\l{ValueVisitor}s and a do-nothing getSourceLocation().
Value instances should be cast to a derived type either through the
asXXX() helper functions such as asNumberValue() or via the
value_cast() template function.
Values are the result of many operations in the QmlJS code model:
\list
\o \l{Evaluate}
\o Context::lookupType() and Context::lookupReference()
\o ScopeChain::lookup()
\o ObjectValue::lookupMember()
\endlist
*/
namespace {
class LookupMember: public MemberProcessor
{
QString _name;
const Value *_value;
bool process(const QString &name, const Value *value)
{
if (_value)
return false;
if (name == _name) {
_value = value;
return false;
}
return true;
}
public:
LookupMember(const QString &name)
: _name(name), _value(0) {}
const Value *value() const { return _value; }
virtual bool processProperty(const QString &name, const Value *value)
{
return process(name, value);
}
virtual bool processEnumerator(const QString &name, const Value *value)
{
return process(name, value);
}
virtual bool processSignal(const QString &name, const Value *value)
{
return process(name, value);
}
virtual bool processSlot(const QString &name, const Value *value)
{
return process(name, value);
}
virtual bool processGeneratedSlot(const QString &name, const Value *value)
{
return process(name, value);
}
};
class MetaFunction: public FunctionValue
{
FakeMetaMethod _method;
public:
MetaFunction(const FakeMetaMethod &method, ValueOwner *valueOwner)
: FunctionValue(valueOwner), _method(method)
{
}
virtual int namedArgumentCount() const
{
return _method.parameterNames().size();
}
virtual QString argumentName(int index) const
{
if (index < _method.parameterNames().size())
return _method.parameterNames().at(index);
return FunctionValue::argumentName(index);
}
virtual bool isVariadic() const
{
return false;
}
};
} // end of anonymous namespace
CppComponentValue::CppComponentValue(FakeMetaObject::ConstPtr metaObject, const QString &className,
const QString &packageName, const ComponentVersion &componentVersion,
const ComponentVersion &importVersion, int metaObjectRevision,
ValueOwner *valueOwner)
: ObjectValue(valueOwner),
_metaObject(metaObject),
_moduleName(packageName),
_componentVersion(componentVersion),
_importVersion(importVersion),
_metaObjectRevision(metaObjectRevision)
{
setClassName(className);
int nEnums = metaObject->enumeratorCount();
for (int i = 0; i < nEnums; ++i) {
FakeMetaEnum fEnum = metaObject->enumerator(i);
_enums[fEnum.name()] = new QmlEnumValue(this, i);
}
}
CppComponentValue::~CppComponentValue()
{
delete _metaSignatures;
delete _signalScopes;
}
static QString generatedSlotName(const QString &base)
{
QString slotName = QLatin1String("on");
slotName += base.at(0).toUpper();
slotName += base.midRef(1);
return slotName;
}
const CppComponentValue *CppComponentValue::asCppComponentValue() const
{
return this;
}
void CppComponentValue::processMembers(MemberProcessor *processor) const
{
// process the meta enums
for (int index = _metaObject->enumeratorOffset(); index < _metaObject->enumeratorCount(); ++index) {
FakeMetaEnum e = _metaObject->enumerator(index);
for (int i = 0; i < e.keyCount(); ++i) {
processor->processEnumerator(e.key(i), valueOwner()->numberValue());
}
}
// all explicitly defined signal names
QSet<QString> explicitSignals;
// make MetaFunction instances lazily when first needed
QList<const Value *> *signatures = _metaSignatures;
if (!signatures) {
signatures = new QList<const Value *>;
signatures->reserve(_metaObject->methodCount());
for (int index = 0; index < _metaObject->methodCount(); ++index)
signatures->append(new MetaFunction(_metaObject->method(index), valueOwner()));
if (!_metaSignatures.testAndSetOrdered(0, signatures)) {
delete signatures;
signatures = _metaSignatures;
}
}
// process the meta methods
for (int index = 0; index < _metaObject->methodCount(); ++index) {
const FakeMetaMethod method = _metaObject->method(index);
if (_metaObjectRevision < method.revision())
continue;
const QString &methodName = _metaObject->method(index).methodName();
const Value *signature = signatures->at(index);
if (method.methodType() == FakeMetaMethod::Slot && method.access() == FakeMetaMethod::Public) {
processor->processSlot(methodName, signature);
} else if (method.methodType() == FakeMetaMethod::Signal && method.access() != FakeMetaMethod::Private) {
// process the signal
processor->processSignal(methodName, signature);
explicitSignals.insert(methodName);
// process the generated slot
const QString &slotName = generatedSlotName(methodName);
processor->processGeneratedSlot(slotName, signature);
}
}
// process the meta properties
for (int index = 0; index < _metaObject->propertyCount(); ++index) {
const FakeMetaProperty prop = _metaObject->property(index);
if (_metaObjectRevision < prop.revision())
continue;
const QString propertyName = prop.name();
processor->processProperty(propertyName, valueForCppName(prop.typeName()));
// every property always has a onXyzChanged slot, even if the NOTIFY
// signal has a different name
QString signalName = propertyName;
signalName += QLatin1String("Changed");
if (!explicitSignals.contains(signalName)) {
// process the generated slot
const QString &slotName = generatedSlotName(signalName);
processor->processGeneratedSlot(slotName, valueOwner()->unknownValue());
}
}
// look into attached types
const QString &attachedTypeName = _metaObject->attachedTypeName();
if (!attachedTypeName.isEmpty()) {
const CppComponentValue *attachedType = valueOwner()->cppQmlTypes().objectByCppName(attachedTypeName);
if (attachedType)
attachedType->processMembers(processor);
}
ObjectValue::processMembers(processor);
}
const Value *CppComponentValue::valueForCppName(const QString &typeName) const
{
const CppQmlTypes &cppTypes = valueOwner()->cppQmlTypes();
// check in the same package/version first
const CppComponentValue *objectValue = cppTypes.objectByQualifiedName(
_moduleName, typeName, _importVersion);
if (objectValue)
return objectValue;
// fallback to plain cpp name
objectValue = cppTypes.objectByCppName(typeName);
if (objectValue)
return objectValue;
// try qml builtin type names
if (const Value *v = valueOwner()->defaultValueForBuiltinType(typeName)) {
if (!v->asUndefinedValue())
return v;
}
// map other C++ types
if (typeName == QLatin1String("QByteArray")
|| typeName == QLatin1String("QString")) {
return valueOwner()->stringValue();
} else if (typeName == QLatin1String("QUrl")) {
return valueOwner()->urlValue();
} else if (typeName == QLatin1String("long")) {
return valueOwner()->intValue();
} else if (typeName == QLatin1String("float")
|| typeName == QLatin1String("qreal")) {
return valueOwner()->realValue();
} else if (typeName == QLatin1String("QFont")) {
return valueOwner()->qmlFontObject();
} else if (typeName == QLatin1String("QPoint")
|| typeName == QLatin1String("QPointF")
|| typeName == QLatin1String("QVector2D")) {
return valueOwner()->qmlPointObject();
} else if (typeName == QLatin1String("QSize")
|| typeName == QLatin1String("QSizeF")) {
return valueOwner()->qmlSizeObject();
} else if (typeName == QLatin1String("QRect")
|| typeName == QLatin1String("QRectF")) {
return valueOwner()->qmlRectObject();
} else if (typeName == QLatin1String("QVector3D")) {
return valueOwner()->qmlVector3DObject();
} else if (typeName == QLatin1String("QColor")) {
return valueOwner()->colorValue();
} else if (typeName == QLatin1String("QDeclarativeAnchorLine")) {
return valueOwner()->anchorLineValue();
}
// might be an enum
const CppComponentValue *base = this;
const QStringList components = typeName.split(QLatin1String("::"));
if (components.size() == 2) {
base = valueOwner()->cppQmlTypes().objectByCppName(components.first());
}
if (base) {
if (const QmlEnumValue *value = base->getEnumValue(components.last()))
return value;
}
// may still be a cpp based value
return valueOwner()->unknownValue();
}
const CppComponentValue *CppComponentValue::prototype() const
{
Q_ASSERT(!_prototype || value_cast<CppComponentValue>(_prototype));
return static_cast<const CppComponentValue *>(_prototype);
}
/*!
\returns a list started by this object and followed by all its prototypes
Prefer to use this over calling prototype() in a loop, as it avoids cycles.
*/
QList<const CppComponentValue *> CppComponentValue::prototypes() const
{
QList<const CppComponentValue *> protos;
for (const CppComponentValue *it = this; it; it = it->prototype()) {
if (protos.contains(it))
break;
protos += it;
}
return protos;
}
FakeMetaObject::ConstPtr CppComponentValue::metaObject() const
{
return _metaObject;
}
QString CppComponentValue::moduleName() const
{ return _moduleName; }
ComponentVersion CppComponentValue::componentVersion() const
{ return _componentVersion; }
ComponentVersion CppComponentValue::importVersion() const
{ return _importVersion; }
QString CppComponentValue::defaultPropertyName() const
{ return _metaObject->defaultPropertyName(); }
QString CppComponentValue::propertyType(const QString &propertyName) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
int propIdx = iter->propertyIndex(propertyName);
if (propIdx != -1) {
return iter->property(propIdx).typeName();
}
}
return QString();
}
bool CppComponentValue::isListProperty(const QString &propertyName) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
int propIdx = iter->propertyIndex(propertyName);
if (propIdx != -1) {
return iter->property(propIdx).isList();
}
}
return false;
}
FakeMetaEnum CppComponentValue::getEnum(const QString &typeName, const CppComponentValue **foundInScope) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
const int index = iter->enumeratorIndex(typeName);
if (index != -1) {
if (foundInScope)
*foundInScope = it;
return iter->enumerator(index);
}
}
if (foundInScope)
*foundInScope = 0;
return FakeMetaEnum();
}
const QmlEnumValue *CppComponentValue::getEnumValue(const QString &typeName, const CppComponentValue **foundInScope) const
{
foreach (const CppComponentValue *it, prototypes()) {
if (const QmlEnumValue *e = it->_enums.value(typeName)) {
if (foundInScope)
*foundInScope = it;
return e;
}
}
if (foundInScope)
*foundInScope = 0;
return 0;
}
const ObjectValue *CppComponentValue::signalScope(const QString &signalName) const
{
QHash<QString, const ObjectValue *> *scopes = _signalScopes;
if (!scopes) {
scopes = new QHash<QString, const ObjectValue *>;
// usually not all methods are signals
scopes->reserve(_metaObject->methodCount() / 2);
for (int index = 0; index < _metaObject->methodCount(); ++index) {
const FakeMetaMethod &method = _metaObject->method(index);
if (method.methodType() != FakeMetaMethod::Signal || method.access() == FakeMetaMethod::Private)
continue;
const QStringList ¶meterNames = method.parameterNames();
const QStringList ¶meterTypes = method.parameterTypes();
QTC_ASSERT(parameterNames.size() == parameterTypes.size(), continue);
ObjectValue *scope = valueOwner()->newObject(/*prototype=*/0);
for (int i = 0; i < parameterNames.size(); ++i) {
const QString &name = parameterNames.at(i);
const QString &type = parameterTypes.at(i);
if (name.isEmpty())
continue;
scope->setMember(name, valueForCppName(type));
}
scopes->insert(generatedSlotName(method.methodName()), scope);
}
if (!_signalScopes.testAndSetOrdered(0, scopes)) {
delete _signalScopes;
scopes = _signalScopes;
}
}
return scopes->value(signalName);
}
bool CppComponentValue::isWritable(const QString &propertyName) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
int propIdx = iter->propertyIndex(propertyName);
if (propIdx != -1) {
return iter->property(propIdx).isWritable();
}
}
return false;
}
bool CppComponentValue::isPointer(const QString &propertyName) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
int propIdx = iter->propertyIndex(propertyName);
if (propIdx != -1) {
return iter->property(propIdx).isPointer();
}
}
return false;
}
bool CppComponentValue::hasLocalProperty(const QString &typeName) const
{
int idx = _metaObject->propertyIndex(typeName);
if (idx == -1)
return false;
return true;
}
bool CppComponentValue::hasProperty(const QString &propertyName) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
int propIdx = iter->propertyIndex(propertyName);
if (propIdx != -1) {
return true;
}
}
return false;
}
bool CppComponentValue::isDerivedFrom(FakeMetaObject::ConstPtr base) const
{
foreach (const CppComponentValue *it, prototypes()) {
FakeMetaObject::ConstPtr iter = it->_metaObject;
if (iter == base)
return true;
}
return false;
}
QmlEnumValue::QmlEnumValue(const CppComponentValue *owner, int enumIndex)
: _owner(owner)
, _enumIndex(enumIndex)
{
owner->valueOwner()->registerValue(this);
}
QmlEnumValue::~QmlEnumValue()
{
}
const QmlEnumValue *QmlEnumValue::asQmlEnumValue() const
{
return this;
}
QString QmlEnumValue::name() const
{
return _owner->metaObject()->enumerator(_enumIndex).name();
}
QStringList QmlEnumValue::keys() const
{
return _owner->metaObject()->enumerator(_enumIndex).keys();
}
const CppComponentValue *QmlEnumValue::owner() const
{
return _owner;
}
////////////////////////////////////////////////////////////////////////////////
// ValueVisitor
////////////////////////////////////////////////////////////////////////////////
ValueVisitor::ValueVisitor()
{
}
ValueVisitor::~ValueVisitor()
{
}
void ValueVisitor::visit(const NullValue *)
{
}
void ValueVisitor::visit(const UndefinedValue *)
{
}
void ValueVisitor::visit(const UnknownValue *)
{
}
void ValueVisitor::visit(const NumberValue *)
{
}
void ValueVisitor::visit(const BooleanValue *)
{
}
void ValueVisitor::visit(const StringValue *)
{
}
void ValueVisitor::visit(const ObjectValue *)
{
}
void ValueVisitor::visit(const FunctionValue *)
{
}
void ValueVisitor::visit(const Reference *)
{
}
void ValueVisitor::visit(const ColorValue *)
{
}
void ValueVisitor::visit(const AnchorLineValue *)
{
}
////////////////////////////////////////////////////////////////////////////////
// Value
////////////////////////////////////////////////////////////////////////////////
Value::Value()
{
}
Value::~Value()
{
}
bool Value::getSourceLocation(QString *, int *, int *) const
{
return false;
}
const NullValue *Value::asNullValue() const
{
return 0;
}
const UndefinedValue *Value::asUndefinedValue() const
{
return 0;
}
const UnknownValue *Value::asUnknownValue() const
{
return 0;
}
const NumberValue *Value::asNumberValue() const
{
return 0;
}
const IntValue *Value::asIntValue() const
{
return 0;
}
const RealValue *Value::asRealValue() const
{
return 0;
}
const BooleanValue *Value::asBooleanValue() const
{
return 0;
}
const StringValue *Value::asStringValue() const
{
return 0;
}
const UrlValue *Value::asUrlValue() const
{
return 0;
}
const ObjectValue *Value::asObjectValue() const
{
return 0;
}
const FunctionValue *Value::asFunctionValue() const
{
return 0;
}
const Reference *Value::asReference() const
{
return 0;
}
const ColorValue *Value::asColorValue() const
{
return 0;
}
const AnchorLineValue *Value::asAnchorLineValue() const
{
return 0;
}
const CppComponentValue *Value::asCppComponentValue() const
{
return 0;
}
const ASTObjectValue *Value::asAstObjectValue() const
{
return 0;
}
const QmlEnumValue *Value::asQmlEnumValue() const
{
return 0;
}
const QmlPrototypeReference *Value::asQmlPrototypeReference() const
{
return 0;
}
const ASTPropertyReference *Value::asAstPropertyReference() const
{
return 0;
}
const ASTSignal *Value::asAstSignal() const
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Values
////////////////////////////////////////////////////////////////////////////////
const NullValue *NullValue::asNullValue() const
{
return this;
}
void NullValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const UndefinedValue *UndefinedValue::asUndefinedValue() const
{
return this;
}
void UnknownValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const UnknownValue *UnknownValue::asUnknownValue() const
{
return this;
}
void UndefinedValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const NumberValue *NumberValue::asNumberValue() const
{
return this;
}
const RealValue *RealValue::asRealValue() const
{
return this;
}
const IntValue *IntValue::asIntValue() const
{
return this;
}
void NumberValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const BooleanValue *BooleanValue::asBooleanValue() const
{
return this;
}
void BooleanValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const StringValue *StringValue::asStringValue() const
{
return this;
}
const UrlValue *UrlValue::asUrlValue() const
{
return this;
}
void StringValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
Reference::Reference(ValueOwner *valueOwner)
: _valueOwner(valueOwner)
{
_valueOwner->registerValue(this);
}
Reference::~Reference()
{
}
ValueOwner *Reference::valueOwner() const
{
return _valueOwner;
}
const Reference *Reference::asReference() const
{
return this;
}
void Reference::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const Value *Reference::value(ReferenceContext *) const
{
return _valueOwner->undefinedValue();
}
void ColorValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const ColorValue *ColorValue::asColorValue() const
{
return this;
}
void AnchorLineValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const AnchorLineValue *AnchorLineValue::asAnchorLineValue() const
{
return this;
}
MemberProcessor::MemberProcessor()
{
}
MemberProcessor::~MemberProcessor()
{
}
bool MemberProcessor::processProperty(const QString &, const Value *)
{
return true;
}
bool MemberProcessor::processEnumerator(const QString &, const Value *)
{
return true;
}
bool MemberProcessor::processSignal(const QString &, const Value *)
{
return true;
}
bool MemberProcessor::processSlot(const QString &, const Value *)
{
return true;
}
bool MemberProcessor::processGeneratedSlot(const QString &, const Value *)
{
return true;
}
ObjectValue::ObjectValue(ValueOwner *valueOwner)
: _valueOwner(valueOwner),
_prototype(0)
{
valueOwner->registerValue(this);
}
ObjectValue::~ObjectValue()
{
}
ValueOwner *ObjectValue::valueOwner() const
{
return _valueOwner;
}
QString ObjectValue::className() const
{
return _className;
}
void ObjectValue::setClassName(const QString &className)
{
_className = className;
}
const Value *ObjectValue::prototype() const
{
return _prototype;
}
const ObjectValue *ObjectValue::prototype(const Context *context) const
{
const ObjectValue *prototypeObject = value_cast<ObjectValue>(_prototype);
if (! prototypeObject) {
if (const Reference *prototypeReference = value_cast<Reference>(_prototype)) {
prototypeObject = value_cast<ObjectValue>(context->lookupReference(prototypeReference));
}
}
return prototypeObject;
}
void ObjectValue::setPrototype(const Value *prototype)
{
_prototype = prototype;
}
void ObjectValue::setMember(const QString &name, const Value *value)
{
_members[name] = value;
}
void ObjectValue::removeMember(const QString &name)
{
_members.remove(name);
}
const ObjectValue *ObjectValue::asObjectValue() const
{
return this;
}
void ObjectValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
bool ObjectValue::checkPrototype(const ObjectValue *, QSet<const ObjectValue *> *) const
{
#if 0
const int previousSize = processed->size();
processed->insert(this);
if (previousSize != processed->size()) {
if (this == proto)
return false;
if (prototype() && ! prototype()->checkPrototype(proto, processed))
return false;
return true;
}
#endif
return false;
}
void ObjectValue::processMembers(MemberProcessor *processor) const
{
QHashIterator<QString, const Value *> it(_members);
while (it.hasNext()) {
it.next();
if (! processor->processProperty(it.key(), it.value()))
break;
}
}
const Value *ObjectValue::lookupMember(const QString &name, const Context *context,
const ObjectValue **foundInObject,
bool examinePrototypes) const
{
if (const Value *m = _members.value(name)) {
if (foundInObject)
*foundInObject = this;
return m;
} else {
LookupMember slowLookup(name);
processMembers(&slowLookup);
if (slowLookup.value()) {
if (foundInObject)
*foundInObject = this;
return slowLookup.value();
}
}
if (examinePrototypes && context) {
PrototypeIterator iter(this, context);
iter.next(); // skip this
while (iter.hasNext()) {
const ObjectValue *prototypeObject = iter.next();
if (const Value *m = prototypeObject->lookupMember(name, context, foundInObject, false))
return m;
}
}
if (foundInObject)
*foundInObject = 0;
return 0;
}
PrototypeIterator::PrototypeIterator(const ObjectValue *start, const Context *context)
: m_current(0)
, m_next(start)
, m_context(context)
, m_error(NoError)
{
if (start)
m_prototypes.reserve(10);
}
PrototypeIterator::PrototypeIterator(const ObjectValue *start, const ContextPtr &context)
: m_current(0)
, m_next(start)
, m_context(context.data())
, m_error(NoError)
{
if (start)
m_prototypes.reserve(10);
}
bool PrototypeIterator::hasNext()
{
if (m_next)
return true;
if (!m_current)
return false;
const Value *proto = m_current->prototype();
if (!proto)
return false;
m_next = value_cast<ObjectValue>(proto);
if (! m_next)
m_next = value_cast<ObjectValue>(m_context->lookupReference(proto));
if (!m_next) {
m_error = ReferenceResolutionError;
return false;
}
if (m_prototypes.contains(m_next)) {
m_error = CycleError;
m_next = 0;
return false;
}
return true;
}
const ObjectValue *PrototypeIterator::next()
{
if (hasNext()) {
m_current = m_next;
m_prototypes += m_next;
m_next = 0;
return m_current;
}
return 0;
}
const ObjectValue *PrototypeIterator::peekNext()
{
if (hasNext()) {
return m_next;
}
return 0;
}
PrototypeIterator::Error PrototypeIterator::error() const
{
return m_error;
}
QList<const ObjectValue *> PrototypeIterator::all()
{
while (hasNext())
next();
return m_prototypes;
}
FunctionValue::FunctionValue(ValueOwner *valueOwner)
: ObjectValue(valueOwner)
{
setClassName("Function");
setMember(QLatin1String("length"), valueOwner->numberValue());
setPrototype(valueOwner->functionPrototype());
}
FunctionValue::~FunctionValue()
{
}
const Value *FunctionValue::returnValue() const
{
return valueOwner()->unknownValue();
}
int FunctionValue::namedArgumentCount() const
{
return 0;
}
const Value *FunctionValue::argument(int) const
{
return valueOwner()->unknownValue();
}
QString FunctionValue::argumentName(int index) const
{
return QString::fromLatin1("arg%1").arg(index + 1);
}
int FunctionValue::optionalNamedArgumentCount() const
{
return 0;
}
bool FunctionValue::isVariadic() const
{
return true;
}
const FunctionValue *FunctionValue::asFunctionValue() const
{
return this;
}
void FunctionValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
Function::Function(ValueOwner *valueOwner)
: FunctionValue(valueOwner)
, _returnValue(0)
, _optionalNamedArgumentCount(0)
, _isVariadic(false)
{
}
Function::~Function()
{
}
void Function::addArgument(const Value *argument, const QString &name)
{
if (!name.isEmpty()) {
while (_argumentNames.size() < _arguments.size())
_argumentNames.push_back(QString());
_argumentNames.push_back(name);
}
_arguments.push_back(argument);
}
const Value *Function::returnValue() const
{
return _returnValue;
}
void Function::setReturnValue(const Value *returnValue)
{
_returnValue = returnValue;
}
void Function::setVariadic(bool variadic)
{
_isVariadic = variadic;
}
void Function::setOptionalNamedArgumentCount(int count)
{
_optionalNamedArgumentCount = count;
}
int Function::namedArgumentCount() const
{
return _arguments.size();
}
int Function::optionalNamedArgumentCount() const
{
return _optionalNamedArgumentCount;
}
const Value *Function::argument(int index) const
{
return _arguments.at(index);
}
QString Function::argumentName(int index) const
{
if (index < _argumentNames.size()) {
const QString name = _argumentNames.at(index);
if (!name.isEmpty())
return _argumentNames.at(index);
}
return FunctionValue::argumentName(index);
}
bool Function::isVariadic() const
{
return _isVariadic;
}
////////////////////////////////////////////////////////////////////////////////
// typing environment
////////////////////////////////////////////////////////////////////////////////
CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultLibraryObjects;
CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultQtObjects;
CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInfoList &qmlTypeFiles, QStringList *errors, QStringList *warnings)
{
QHash<QString, FakeMetaObject::ConstPtr> newObjects;
foreach (const QFileInfo &qmlTypeFile, qmlTypeFiles) {
QString error, warning;
QFile file(qmlTypeFile.absoluteFilePath());
if (file.open(QIODevice::ReadOnly)) {
QByteArray contents = file.readAll();
file.close();
parseQmlTypeDescriptions(contents, &newObjects, 0, &error, &warning);
} else {
error = file.errorString();
}
if (!error.isEmpty()) {
errors->append(TypeDescriptionReader::tr(
"Errors while loading qmltypes from %1:\n%2").arg(
qmlTypeFile.absoluteFilePath(), error));
}
if (!warning.isEmpty()) {
warnings->append(TypeDescriptionReader::tr(
"Warnings while loading qmltypes from %1:\n%2").arg(
qmlTypeFile.absoluteFilePath(), error));
}
}
return newObjects;
}
void CppQmlTypesLoader::parseQmlTypeDescriptions(const QByteArray &xml,
BuiltinObjects *newObjects,
QList<ModuleApiInfo> *newModuleApis,
QString *errorMessage,
QString *warningMessage)
{
errorMessage->clear();
warningMessage->clear();
TypeDescriptionReader reader(QString::fromUtf8(xml));
if (!reader(newObjects, newModuleApis)) {
if (reader.errorMessage().isEmpty()) {
*errorMessage = QLatin1String("unknown error");
} else {
*errorMessage = reader.errorMessage();
}
}
*warningMessage = reader.warningMessage();
}
CppQmlTypes::CppQmlTypes(ValueOwner *valueOwner)
: _cppContextProperties(0)
, _valueOwner(valueOwner)
{
}
const QLatin1String CppQmlTypes::defaultPackage("<default>");
const QLatin1String CppQmlTypes::cppPackage("<cpp>");
template <typename T>
void CppQmlTypes::load(const T &fakeMetaObjects, const QString &overridePackage)
{
QList<CppComponentValue *> newCppTypes;
foreach (const FakeMetaObject::ConstPtr &fmo, fakeMetaObjects) {
foreach (const FakeMetaObject::Export &exp, fmo->exports()) {
QString package = exp.package;
if (package.isEmpty())
package = overridePackage;
_fakeMetaObjectsByPackage[package].insert(fmo);
// make versionless cpp types directly
// needed for access to property types that are not exported, like QDeclarativeAnchors
if (exp.package == cppPackage) {
QTC_ASSERT(exp.version == ComponentVersion(), continue);
QTC_ASSERT(exp.type == fmo->className(), continue);
CppComponentValue *cppValue = new CppComponentValue(
fmo, fmo->className(), cppPackage, ComponentVersion(), ComponentVersion(),
ComponentVersion::MaxVersion, _valueOwner);
_objectsByQualifiedName[qualifiedName(cppPackage, fmo->className(), ComponentVersion())] = cppValue;
newCppTypes += cppValue;
}
}
}
// set prototypes of cpp types
foreach (CppComponentValue *object, newCppTypes) {
const QString &protoCppName = object->metaObject()->superclassName();
const CppComponentValue *proto = objectByCppName(protoCppName);
if (proto)
object->setPrototype(proto);
}
}
// explicitly instantiate load for list and hash
template void CppQmlTypes::load< QList<FakeMetaObject::ConstPtr> >(const QList<FakeMetaObject::ConstPtr> &, const QString &);
template void CppQmlTypes::load< QHash<QString, FakeMetaObject::ConstPtr> >(const QHash<QString, FakeMetaObject::ConstPtr> &, const QString &);
QList<const CppComponentValue *> CppQmlTypes::createObjectsForImport(const QString &package, ComponentVersion version)
{
QList<const CppComponentValue *> exportedObjects;
QList<const CppComponentValue *> newObjects;
// make new exported objects
foreach (const FakeMetaObject::ConstPtr &fmo, _fakeMetaObjectsByPackage.value(package)) {
// find the highest-version export
FakeMetaObject::Export bestExport;
foreach (const FakeMetaObject::Export &exp, fmo->exports()) {
if (exp.package != package || (version.isValid() && exp.version > version))
continue;
if (!bestExport.isValid() || exp.version > bestExport.version)
bestExport = exp;
}
if (!bestExport.isValid())
continue;
// if it already exists, skip
const QString key = qualifiedName(package, fmo->className(), version);
if (_objectsByQualifiedName.contains(key))
continue;
QString name = bestExport.type;
bool exported = true;
if (name.isEmpty()) {
exported = false;
name = fmo->className();
}
CppComponentValue *newObject = new CppComponentValue(
fmo, name, package, bestExport.version, version,
bestExport.metaObjectRevision, _valueOwner);
// use package.cppname importversion as key
_objectsByQualifiedName.insert(key, newObject);
if (exported)
exportedObjects += newObject;
newObjects += newObject;
}
// set their prototypes, creating them if necessary
foreach (const CppComponentValue *cobject, newObjects) {
CppComponentValue *object = const_cast<CppComponentValue *>(cobject);
while (!object->prototype()) {
const QString &protoCppName = object->metaObject()->superclassName();
if (protoCppName.isEmpty())
break;
// if the prototype already exists, done
const QString key = qualifiedName(object->moduleName(), protoCppName, version);
if (const CppComponentValue *proto = _objectsByQualifiedName.value(key)) {
object->setPrototype(proto);
break;
}
// get the fmo via the cpp name
const CppComponentValue *cppProto = objectByCppName(protoCppName);
if (!cppProto)
break;
FakeMetaObject::ConstPtr protoFmo = cppProto->metaObject();
// make a new object
CppComponentValue *proto = new CppComponentValue(
protoFmo, protoCppName, object->moduleName(), ComponentVersion(),
object->importVersion(), ComponentVersion::MaxVersion, _valueOwner);
_objectsByQualifiedName.insert(key, proto);
object->setPrototype(proto);
// maybe set prototype of prototype
object = proto;
}
}
return exportedObjects;
}
bool CppQmlTypes::hasModule(const QString &module) const
{
return _fakeMetaObjectsByPackage.contains(module);
}
QString CppQmlTypes::qualifiedName(const QString &module, const QString &type, ComponentVersion version)
{
return QString("%1/%2 %3").arg(
module, type,
version.toString());
}
const CppComponentValue *CppQmlTypes::objectByQualifiedName(const QString &name) const
{
return _objectsByQualifiedName.value(name);
}
const CppComponentValue *CppQmlTypes::objectByQualifiedName(const QString &package, const QString &type,
ComponentVersion version) const
{
return objectByQualifiedName(qualifiedName(package, type, version));
}
const CppComponentValue *CppQmlTypes::objectByCppName(const QString &cppName) const
{
return objectByQualifiedName(qualifiedName(cppPackage, cppName, ComponentVersion()));
}
void CppQmlTypes::setCppContextProperties(const ObjectValue *contextProperties)
{
_cppContextProperties = contextProperties;
}
const ObjectValue *CppQmlTypes::cppContextProperties() const
{
return _cppContextProperties;
}
ConvertToNumber::ConvertToNumber(ValueOwner *valueOwner)
: _valueOwner(valueOwner), _result(0)
{
}
const Value *ConvertToNumber::operator()(const Value *value)
{
const Value *previousValue = switchResult(0);
if (value)
value->accept(this);
return switchResult(previousValue);
}
const Value *ConvertToNumber::switchResult(const Value *value)
{
const Value *previousResult = _result;
_result = value;
return previousResult;
}
void ConvertToNumber::visit(const NullValue *)
{
_result = _valueOwner->numberValue();
}
void ConvertToNumber::visit(const UndefinedValue *)
{
_result = _valueOwner->numberValue();
}
void ConvertToNumber::visit(const NumberValue *value)
{
_result = value;
}
void ConvertToNumber::visit(const BooleanValue *)
{
_result = _valueOwner->numberValue();
}
void ConvertToNumber::visit(const StringValue *)
{
_result = _valueOwner->numberValue();
}
void ConvertToNumber::visit(const ObjectValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<FunctionValue>(object->lookupMember("valueOf", ContextPtr()))) {
_result = value_cast<NumberValue>(valueOfMember->returnValue());
}
}
void ConvertToNumber::visit(const FunctionValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<FunctionValue>(object->lookupMember("valueOf", ContextPtr()))) {
_result = value_cast<NumberValue>(valueOfMember->returnValue());
}
}
ConvertToString::ConvertToString(ValueOwner *valueOwner)
: _valueOwner(valueOwner), _result(0)
{
}
const Value *ConvertToString::operator()(const Value *value)
{
const Value *previousValue = switchResult(0);
if (value)
value->accept(this);
return switchResult(previousValue);
}
const Value *ConvertToString::switchResult(const Value *value)
{
const Value *previousResult = _result;
_result = value;
return previousResult;
}
void ConvertToString::visit(const NullValue *)
{
_result = _valueOwner->stringValue();
}
void ConvertToString::visit(const UndefinedValue *)
{
_result = _valueOwner->stringValue();
}
void ConvertToString::visit(const NumberValue *)
{
_result = _valueOwner->stringValue();
}
void ConvertToString::visit(const BooleanValue *)
{
_result = _valueOwner->stringValue();
}
void ConvertToString::visit(const StringValue *value)
{
_result = value;
}
void ConvertToString::visit(const ObjectValue *object)
{
if (const FunctionValue *toStringMember = value_cast<FunctionValue>(object->lookupMember("toString", ContextPtr()))) {
_result = value_cast<StringValue>(toStringMember->returnValue());
}
}
void ConvertToString::visit(const FunctionValue *object)
{
if (const FunctionValue *toStringMember = value_cast<FunctionValue>(object->lookupMember("toString", ContextPtr()))) {
_result = value_cast<StringValue>(toStringMember->returnValue());
}
}
ConvertToObject::ConvertToObject(ValueOwner *valueOwner)
: _valueOwner(valueOwner), _result(0)
{
}
const Value *ConvertToObject::operator()(const Value *value)
{
const Value *previousValue = switchResult(0);
if (value)
value->accept(this);
return switchResult(previousValue);
}
const Value *ConvertToObject::switchResult(const Value *value)
{
const Value *previousResult = _result;
_result = value;
return previousResult;
}
void ConvertToObject::visit(const NullValue *value)
{
_result = value;
}
void ConvertToObject::visit(const UndefinedValue *)
{
_result = _valueOwner->nullValue();
}
void ConvertToObject::visit(const NumberValue *)
{
_result = _valueOwner->numberCtor()->returnValue();
}
void ConvertToObject::visit(const BooleanValue *)
{
_result = _valueOwner->booleanCtor()->returnValue();
}
void ConvertToObject::visit(const StringValue *)
{
_result = _valueOwner->stringCtor()->returnValue();
}
void ConvertToObject::visit(const ObjectValue *object)
{
_result = object;
}
void ConvertToObject::visit(const FunctionValue *object)
{
_result = object;
}
QString TypeId::operator()(const Value *value)
{
_result = QLatin1String("unknown");
if (value)
value->accept(this);
return _result;
}
void TypeId::visit(const NullValue *)
{
_result = QLatin1String("null");
}
void TypeId::visit(const UndefinedValue *)
{
_result = QLatin1String("undefined");
}
void TypeId::visit(const NumberValue *)
{
_result = QLatin1String("number");
}
void TypeId::visit(const BooleanValue *)
{
_result = QLatin1String("boolean");
}
void TypeId::visit(const StringValue *)
{
_result = QLatin1String("string");
}
void TypeId::visit(const ObjectValue *object)
{
_result = object->className();
if (_result.isEmpty())
_result = QLatin1String("object");
}
void TypeId::visit(const FunctionValue *object)
{
_result = object->className();
if (_result.isEmpty())
_result = QLatin1String("Function");
}
void TypeId::visit(const ColorValue *)
{
_result = QLatin1String("string");
}
void TypeId::visit(const AnchorLineValue *)
{
_result = QLatin1String("AnchorLine");
}
ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName,
UiObjectInitializer *initializer,
const Document *doc,
ValueOwner *valueOwner)
: ObjectValue(valueOwner), _typeName(typeName), _initializer(initializer), _doc(doc), _defaultPropertyRef(0)
{
if (_initializer) {
for (UiObjectMemberList *it = _initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
if (UiPublicMember *def = cast<UiPublicMember *>(member)) {
if (def->type == UiPublicMember::Property && !def->name.isEmpty() && !def->memberType.isEmpty()) {
ASTPropertyReference *ref = new ASTPropertyReference(def, _doc, valueOwner);
_properties.append(ref);
if (def->defaultToken.isValid())
_defaultPropertyRef = ref;
} else if (def->type == UiPublicMember::Signal && !def->name.isEmpty()) {
ASTSignal *ref = new ASTSignal(def, _doc, valueOwner);
_signals.append(ref);
}
}
}
}
}
ASTObjectValue::~ASTObjectValue()
{
}
const ASTObjectValue *ASTObjectValue::asAstObjectValue() const
{
return this;
}
bool ASTObjectValue::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
*line = _typeName->identifierToken.startLine;
*column = _typeName->identifierToken.startColumn;
return true;
}
void ASTObjectValue::processMembers(MemberProcessor *processor) const
{
foreach (ASTPropertyReference *ref, _properties) {
processor->processProperty(ref->ast()->name.toString(), ref);
// ### Should get a different value?
processor->processGeneratedSlot(ref->onChangedSlotName(), ref);
}
foreach (ASTSignal *ref, _signals) {
processor->processSignal(ref->ast()->name.toString(), ref);
// ### Should get a different value?
processor->processGeneratedSlot(ref->slotName(), ref);
}
ObjectValue::processMembers(processor);
}
QString ASTObjectValue::defaultPropertyName() const
{
if (_defaultPropertyRef) {
UiPublicMember *prop = _defaultPropertyRef->ast();
if (prop)
return prop->name.toString();
}
return QString();
}
UiObjectInitializer *ASTObjectValue::initializer() const
{
return _initializer;
}
UiQualifiedId *ASTObjectValue::typeName() const
{
return _typeName;
}
const Document *ASTObjectValue::document() const
{
return _doc;
}
ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, const Document *doc, ValueOwner *valueOwner)
: Reference(valueOwner)
, _ast(ast)
, _doc(doc)
{
}
ASTVariableReference::~ASTVariableReference()
{
}
const Value *ASTVariableReference::value(ReferenceContext *referenceContext) const
{
// may be assigned to later
if (!_ast->expression)
return valueOwner()->unknownValue();
Document::Ptr doc = _doc->ptr();
ScopeChain scopeChain(doc, referenceContext->context());
ScopeBuilder builder(&scopeChain);
builder.push(ScopeAstPath(doc)(_ast->expression->firstSourceLocation().begin()));
Evaluate evaluator(&scopeChain, referenceContext);
return evaluator(_ast->expression);
}
bool ASTVariableReference::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
*line = _ast->identifierToken.startLine;
*column = _ast->identifierToken.startColumn;
return true;
}
namespace {
class UsesArgumentsArray : protected Visitor
{
bool _usesArgumentsArray;
public:
bool operator()(FunctionBody *ast)
{
if (!ast || !ast->elements)
return false;
_usesArgumentsArray = false;
Node::accept(ast->elements, this);
return _usesArgumentsArray;
}
protected:
bool visit(ArrayMemberExpression *ast)
{
if (IdentifierExpression *idExp = cast<IdentifierExpression *>(ast->base)) {
if (idExp->name == QLatin1String("arguments"))
_usesArgumentsArray = true;
}
return true;
}
// don't go into nested functions
bool visit(FunctionBody *) { return false; }
};
} // anonymous namespace
ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const Document *doc, ValueOwner *valueOwner)
: FunctionValue(valueOwner)
, _ast(ast)
, _doc(doc)
{
setPrototype(valueOwner->functionPrototype());
for (FormalParameterList *it = ast->formals; it; it = it->next)
_argumentNames.append(it->name.toString());
_isVariadic = UsesArgumentsArray()(ast->body);
}
ASTFunctionValue::~ASTFunctionValue()
{
}
FunctionExpression *ASTFunctionValue::ast() const
{
return _ast;
}
int ASTFunctionValue::namedArgumentCount() const
{
return _argumentNames.size();
}
QString ASTFunctionValue::argumentName(int index) const
{
if (index < _argumentNames.size()) {
const QString &name = _argumentNames.at(index);
if (!name.isEmpty())
return name;
}
return FunctionValue::argumentName(index);
}
bool ASTFunctionValue::isVariadic() const
{
return _isVariadic;
}
bool ASTFunctionValue::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
*line = _ast->identifierToken.startLine;
*column = _ast->identifierToken.startColumn;
return true;
}
QmlPrototypeReference::QmlPrototypeReference(UiQualifiedId *qmlTypeName, const Document *doc,
ValueOwner *valueOwner)
: Reference(valueOwner),
_qmlTypeName(qmlTypeName),
_doc(doc)
{
}
QmlPrototypeReference::~QmlPrototypeReference()
{
}
const QmlPrototypeReference *QmlPrototypeReference::asQmlPrototypeReference() const
{
return this;
}
UiQualifiedId *QmlPrototypeReference::qmlTypeName() const
{
return _qmlTypeName;
}
const Value *QmlPrototypeReference::value(ReferenceContext *referenceContext) const
{
return referenceContext->context()->lookupType(_doc, _qmlTypeName);
}
ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner)
: Reference(valueOwner), _ast(ast), _doc(doc)
{
const QString &propertyName = ast->name.toString();
_onChangedSlotName = generatedSlotName(propertyName);
_onChangedSlotName += QLatin1String("Changed");
}
ASTPropertyReference::~ASTPropertyReference()
{
}
const ASTPropertyReference *ASTPropertyReference::asAstPropertyReference() const
{
return this;
}
bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
*line = _ast->identifierToken.startLine;
*column = _ast->identifierToken.startColumn;
return true;
}
const Value *ASTPropertyReference::value(ReferenceContext *referenceContext) const
{
if (_ast->statement
&& (_ast->memberType.isEmpty()
|| _ast->memberType == QLatin1String("variant")
|| _ast->memberType == QLatin1String("var")
|| _ast->memberType == QLatin1String("alias"))) {
// Adjust the context for the current location - expensive!
// ### Improve efficiency by caching the 'use chain' constructed in ScopeBuilder.
Document::Ptr doc = _doc->ptr();
ScopeChain scopeChain(doc, referenceContext->context());
ScopeBuilder builder(&scopeChain);
int offset = _ast->statement->firstSourceLocation().begin();
builder.push(ScopeAstPath(doc)(offset));
Evaluate evaluator(&scopeChain, referenceContext);
return evaluator(_ast->statement);
}
return valueOwner()->defaultValueForBuiltinType(_ast->memberType.toString());
}
ASTSignal::ASTSignal(UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner)
: FunctionValue(valueOwner), _ast(ast), _doc(doc)
{
const QString &signalName = ast->name.toString();
_slotName = generatedSlotName(signalName);
ObjectValue *v = valueOwner->newObject(/*prototype=*/0);
for (UiParameterList *it = ast->parameters; it; it = it->next) {
if (!it->name.isEmpty())
v->setMember(it->name.toString(), valueOwner->defaultValueForBuiltinType(it->type.toString()));
}
_bodyScope = v;
}
ASTSignal::~ASTSignal()
{
}
const ASTSignal *ASTSignal::asAstSignal() const
{
return this;
}
int ASTSignal::namedArgumentCount() const
{
int count = 0;
for (UiParameterList *it = _ast->parameters; it; it = it->next)
++count;
return count;
}
const Value *ASTSignal::argument(int index) const
{
UiParameterList *param = _ast->parameters;
for (int i = 0; param && i < index; ++i)
param = param->next;
if (!param || param->type.isEmpty())
return valueOwner()->unknownValue();
return valueOwner()->defaultValueForBuiltinType(param->type.toString());
}
QString ASTSignal::argumentName(int index) const
{
UiParameterList *param = _ast->parameters;
for (int i = 0; param && i < index; ++i)
param = param->next;
if (!param || param->name.isEmpty())
return FunctionValue::argumentName(index);
return param->name.toString();
}
bool ASTSignal::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
*line = _ast->identifierToken.startLine;
*column = _ast->identifierToken.startColumn;
return true;
}
ImportInfo::ImportInfo()
: _type(InvalidImport)
, _ast(0)
{
}
ImportInfo ImportInfo::moduleImport(QString uri, ComponentVersion version,
const QString &as, UiImport *ast)
{
// treat Qt 4.7 as QtQuick 1.0
if (uri == QLatin1String("Qt") && version == ComponentVersion(4, 7)) {
uri = QLatin1String("QtQuick");
version = ComponentVersion(1, 0);
}
ImportInfo info;
info._type = LibraryImport;
info._name = uri;
info._path = uri;
info._path.replace(QLatin1Char('.'), QDir::separator());
info._version = version;
info._as = as;
info._ast = ast;
return info;
}
ImportInfo ImportInfo::pathImport(const QString &docPath, const QString &path,
ComponentVersion version, const QString &as, UiImport *ast)
{
ImportInfo info;
info._name = path;
QFileInfo importFileInfo(path);
if (!importFileInfo.isAbsolute()) {
importFileInfo = QFileInfo(docPath + QDir::separator() + path);
}
info._path = importFileInfo.absoluteFilePath();
if (importFileInfo.isFile()) {
info._type = FileImport;
} else if (importFileInfo.isDir()) {
info._type = DirectoryImport;
} else {
info._type = UnknownFileImport;
}
info._version = version;
info._as = as;
info._ast = ast;
return info;
}
ImportInfo ImportInfo::invalidImport(UiImport *ast)
{
ImportInfo info;
info._type = InvalidImport;
info._ast = ast;
return info;
}
ImportInfo ImportInfo::implicitDirectoryImport(const QString &directory)
{
ImportInfo info;
info._type = ImplicitDirectoryImport;
info._path = directory;
return info;
}
bool ImportInfo::isValid() const
{
return _type != InvalidImport;
}
ImportInfo::Type ImportInfo::type() const
{
return _type;
}
QString ImportInfo::name() const
{
return _name;
}
QString ImportInfo::path() const
{
return _path;
}
QString ImportInfo::as() const
{
return _as;
}
ComponentVersion ImportInfo::version() const
{
return _version;
}
UiImport *ImportInfo::ast() const
{
return _ast;
}
Import::Import()
: object(0)
{}
TypeScope::TypeScope(const Imports *imports, ValueOwner *valueOwner)
: ObjectValue(valueOwner)
, _imports(imports)
{
}
const Value *TypeScope::lookupMember(const QString &name, const Context *context,
const ObjectValue **foundInObject, bool) const
{
QListIterator<Import> it(_imports->all());
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
// JS import has no types
if (info.type() == ImportInfo::FileImport)
continue;
if (!info.as().isEmpty()) {
if (info.as() == name) {
if (foundInObject)
*foundInObject = this;
return import;
}
continue;
}
if (const Value *v = import->lookupMember(name, context, foundInObject))
return v;
}
if (foundInObject)
*foundInObject = 0;
return 0;
}
void TypeScope::processMembers(MemberProcessor *processor) const
{
QListIterator<Import> it(_imports->all());
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
// JS import has no types
if (info.type() == ImportInfo::FileImport)
continue;
if (!info.as().isEmpty()) {
processor->processProperty(info.as(), import);
} else {
import->processMembers(processor);
}
}
}
JSImportScope::JSImportScope(const Imports *imports, ValueOwner *valueOwner)
: ObjectValue(valueOwner)
, _imports(imports)
{
}
const Value *JSImportScope::lookupMember(const QString &name, const Context *,
const ObjectValue **foundInObject, bool) const
{
QListIterator<Import> it(_imports->all());
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
// JS imports are always: import "somefile.js" as Foo
if (info.type() != ImportInfo::FileImport)
continue;
if (info.as() == name) {
if (foundInObject)
*foundInObject = this;
return import;
}
}
if (foundInObject)
*foundInObject = 0;
return 0;
}
void JSImportScope::processMembers(MemberProcessor *processor) const
{
QListIterator<Import> it(_imports->all());
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
if (info.type() == ImportInfo::FileImport)
processor->processProperty(info.as(), import);
}
}
Imports::Imports(ValueOwner *valueOwner)
: _typeScope(new TypeScope(this, valueOwner))
, _jsImportScope(new JSImportScope(this, valueOwner))
, _importFailed(false)
{}
void Imports::append(const Import &import)
{
// when doing lookup, imports with 'as' clause are looked at first
if (!import.info.as().isEmpty()) {
_imports.append(import);
} else {
// find first as-import and prepend
for (int i = 0; i < _imports.size(); ++i) {
if (!_imports.at(i).info.as().isEmpty()) {
_imports.insert(i, import);
return;
}
}
// not found, append
_imports.append(import);
}
if (!import.valid)
_importFailed = true;
}
void Imports::setImportFailed()
{
_importFailed = true;
}
ImportInfo Imports::info(const QString &name, const Context *context) const
{
QString firstId = name;
int dotIdx = firstId.indexOf(QLatin1Char('.'));
if (dotIdx != -1)
firstId = firstId.left(dotIdx);
QListIterator<Import> it(_imports);
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
if (!info.as().isEmpty()) {
if (info.as() == firstId)
return info;
continue;
}
if (info.type() == ImportInfo::FileImport) {
if (import->className() == firstId)
return info;
} else {
if (import->lookupMember(firstId, context))
return info;
}
}
return ImportInfo();
}
QString Imports::nameForImportedObject(const ObjectValue *value, const Context *context) const
{
QListIterator<Import> it(_imports);
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
if (info.type() == ImportInfo::FileImport) {
if (import == value)
return import->className();
} else {
const Value *v = import->lookupMember(value->className(), context);
if (v == value) {
QString result = value->className();
if (!info.as().isEmpty()) {
result.prepend(QLatin1Char('.'));
result.prepend(info.as());
}
return result;
}
}
}
return QString();
}
bool Imports::importFailed() const
{
return _importFailed;
}
QList<Import> Imports::all() const
{
return _imports;
}
const TypeScope *Imports::typeScope() const
{
return _typeScope;
}
const JSImportScope *Imports::jsImportScope() const
{
return _jsImportScope;
}
#ifdef QT_DEBUG
class MemberDumper: public MemberProcessor
{
public:
MemberDumper() {}
virtual bool processProperty(const QString &name, const Value *)
{
qDebug() << "property: " << name;
return true;
}
virtual bool processEnumerator(const QString &name, const Value *)
{
qDebug() << "enumerator: " << name;
return true;
}
virtual bool processSignal(const QString &name, const Value *)
{
qDebug() << "signal: " << name;
return true;
}
virtual bool processSlot(const QString &name, const Value *)
{
qDebug() << "slot: " << name;
return true;
}
virtual bool processGeneratedSlot(const QString &name, const Value *)
{
qDebug() << "generated slot: " << name;
return true;
}
};
void Imports::dump() const
{
qDebug() << "Imports contents, in search order:";
QListIterator<Import> it(_imports);
it.toBack();
while (it.hasPrevious()) {
const Import &i = it.previous();
const ObjectValue *import = i.object;
const ImportInfo &info = i.info;
qDebug() << " " << info.path() << " " << info.version().toString() << " as " << info.as() << " : " << import;
MemberDumper dumper;
import->processMembers(&dumper);
}
}
#endif