/************************************************************************** ** ** 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 "qmljsinterpreter.h" #include "qmljsevaluate.h" #include "qmljslink.h" #include "parser/qmljsast_p.h" #include <QtCore/QMetaObject> #include <QtCore/QMetaProperty> #include <QtCore/QDebug> #ifndef NO_DECLARATIVE_BACKEND # include <QtDeclarative/private/qdeclarativemetatype_p.h> # include <QtDeclarative/private/qdeclarativeanchors_p.h> // ### remove me # include <QtDeclarative/private/qdeclarativerectangle_p.h> // ### remove me # include <QtDeclarative/private/qdeclarativevaluetype_p.h> // ### remove me # include <QtDeclarative/private/qdeclarativeanimation_p.h> // ### remove me #endif using namespace QmlJS::Interpreter; using namespace QmlJS::AST; 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); } }; } // end of anonymous namespace #ifndef NO_DECLARATIVE_BACKEND namespace { class MetaFunction: public FunctionValue { QMetaMethod _method; public: MetaFunction(const QMetaMethod &method, Engine *engine) : FunctionValue(engine), _method(method) { } virtual const Value *returnValue() const { return engine()->undefinedValue(); } virtual int argumentCount() const { return _method.parameterNames().size(); } virtual const Value *argument(int) const { return engine()->undefinedValue(); } 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; } virtual const Value *invoke(const Activation *) const { return engine()->undefinedValue(); } }; } // end of anonymous namespace QmlObjectValue::QmlObjectValue(const QMetaObject *metaObject, const QString &qmlTypeName, int majorVersion, int minorVersion, Engine *engine) : ObjectValue(engine), _metaObject(metaObject), _qmlTypeName(qmlTypeName), _majorVersion(majorVersion), _minorVersion(minorVersion) { setClassName(qmlTypeName); // ### TODO: we probably need to do more than just this... } QmlObjectValue::~QmlObjectValue() {} const Value *QmlObjectValue::lookupMember(const QString &name, Context *context) const { return ObjectValue::lookupMember(name, context); } const Value *QmlObjectValue::findOrCreateSignature(int index, const QMetaMethod &method, QString *methodName) const { const QString signature = QString::fromUtf8(method.signature()); const int indexOfParen = signature.indexOf(QLatin1Char('(')); if (indexOfParen == -1) return engine()->undefinedValue(); // skip it, invalid signature. *methodName = signature.left(indexOfParen); const Value *value = _metaSignature.value(index); if (! value) { value = new MetaFunction(method, engine()); _metaSignature.insert(index, value); } return value; } void QmlObjectValue::processMembers(MemberProcessor *processor) const { // process the meta enums for (int index = _metaObject->enumeratorOffset(); index < _metaObject->propertyCount(); ++index) { QMetaEnum e = _metaObject->enumerator(index); for (int i = 0; i < e.keyCount(); ++i) { processor->processEnumerator(QString::fromUtf8(e.key(i)), engine()->numberValue()); } } // process the meta properties for (int index = 0; index < _metaObject->propertyCount(); ++index) { QMetaProperty prop = _metaObject->property(index); processor->processProperty(prop.name(), propertyValue(prop)); } // process the meta methods for (int index = 0; index < _metaObject->methodCount(); ++index) { QMetaMethod method = _metaObject->method(index); QString methodName; const Value *signature = findOrCreateSignature(index, method, &methodName); if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) { processor->processSlot(methodName, signature); } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) { // process the signal processor->processSignal(methodName, signature); QString slotName; slotName += QLatin1String("on"); slotName += methodName.at(0).toUpper(); slotName += methodName.midRef(1); // process the generated slot processor->processGeneratedSlot(slotName, signature); } } ObjectValue::processMembers(processor); } const Value *QmlObjectValue::propertyValue(const QMetaProperty &prop) const { if (QDeclarativeMetaType::isQObject(prop.userType())) { QDeclarativeType *qmlPropertyType = QDeclarativeMetaType::qmlType(prop.userType()); if (qmlPropertyType && !qmlPropertyType->qmlTypeName().isEmpty()) { QString typeName = qmlPropertyType->qmlTypeName(); int slashIdx = typeName.lastIndexOf(QLatin1Char('/')); QString package; if (slashIdx != -1) { package = typeName.left(slashIdx); typeName = typeName.mid(slashIdx + 1); } if (const ObjectValue *objectValue = engine()->newQmlObject(typeName, package, qmlPropertyType->majorVersion(), qmlPropertyType->minorVersion())) return objectValue; } else { QString typeName = QString::fromUtf8(prop.typeName()); if (typeName.endsWith(QLatin1Char('*'))) typeName.truncate(typeName.length() - 1); typeName.replace(QLatin1Char('.'), QLatin1Char('/')); if (const ObjectValue *objectValue = engine()->newQmlObject(typeName, "", -1, -1)) // ### we should extend this to lookup the property types in the QDeclarativeType object, instead of the QMetaProperty. return objectValue; } } const Value *value = engine()->undefinedValue(); switch (prop.type()) { case QMetaType::QByteArray: case QMetaType::QString: case QMetaType::QUrl: value = engine()->stringValue(); break; case QMetaType::Bool: value = engine()->booleanValue(); break; case QMetaType::Int: case QMetaType::Float: case QMetaType::Double: value = engine()->numberValue(); break; case QMetaType::QFont: { // ### cache ObjectValue *object = engine()->newObject(/*prototype =*/ 0); object->setProperty("family", engine()->stringValue()); object->setProperty("weight", engine()->undefinedValue()); // ### make me an object object->setProperty("copitalization", engine()->undefinedValue()); // ### make me an object object->setProperty("bold", engine()->booleanValue()); object->setProperty("italic", engine()->booleanValue()); object->setProperty("underline", engine()->booleanValue()); object->setProperty("overline", engine()->booleanValue()); object->setProperty("strikeout", engine()->booleanValue()); object->setProperty("pointSize", engine()->numberValue()); object->setProperty("pixelSize", engine()->numberValue()); object->setProperty("letterSpacing", engine()->numberValue()); object->setProperty("wordSpacing", engine()->numberValue()); value = object; } break; case QMetaType::QPoint: case QMetaType::QPointF: case QMetaType::QVector2D: { // ### cache ObjectValue *object = engine()->newObject(/*prototype =*/ 0); object->setProperty("x", engine()->numberValue()); object->setProperty("y", engine()->numberValue()); value = object; } break; case QMetaType::QRect: case QMetaType::QRectF: { // ### cache ObjectValue *object = engine()->newObject(/*prototype =*/ 0); object->setProperty("x", engine()->numberValue()); object->setProperty("y", engine()->numberValue()); object->setProperty("width", engine()->numberValue()); object->setProperty("height", engine()->numberValue()); value = object; } break; case QMetaType::QVector3D: { // ### cache ObjectValue *object = engine()->newObject(/*prototype =*/ 0); object->setProperty("x", engine()->numberValue()); object->setProperty("y", engine()->numberValue()); object->setProperty("z", engine()->numberValue()); value = object; } break; case QMetaType::QColor: { value = engine()->colorValue(); } break; default: break; } // end of switch const QString typeName = prop.typeName(); if (typeName == QLatin1String("QDeclarativeAnchorLine")) { value = engine()->anchorLineValue(); } if (value->asStringValue() && prop.name() == QLatin1String("easing") && isDerivedFrom(&QDeclarativePropertyAnimation::staticMetaObject)) { value = engine()->easingCurveNameValue(); } return value; } bool QmlObjectValue::isDerivedFrom(const QMetaObject *base) const { for (const QMetaObject *iter = _metaObject; iter; iter = iter->superClass()) { if (iter == base) return true; } return false; } #endif namespace { //////////////////////////////////////////////////////////////////////////////// // constructors //////////////////////////////////////////////////////////////////////////////// class ObjectCtor: public Function { public: ObjectCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class FunctionCtor: public Function { public: FunctionCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class ArrayCtor: public Function { public: ArrayCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class StringCtor: public Function { public: StringCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class BooleanCtor: public Function { public: BooleanCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class NumberCtor: public Function { public: NumberCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class DateCtor: public Function { public: DateCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; class RegExpCtor: public Function { public: RegExpCtor(Engine *engine); virtual const Value *invoke(const Activation *activation) const; }; ObjectCtor::ObjectCtor(Engine *engine) : Function(engine) { } FunctionCtor::FunctionCtor(Engine *engine) : Function(engine) { } ArrayCtor::ArrayCtor(Engine *engine) : Function(engine) { } StringCtor::StringCtor(Engine *engine) : Function(engine) { } BooleanCtor::BooleanCtor(Engine *engine) : Function(engine) { } NumberCtor::NumberCtor(Engine *engine) : Function(engine) { } DateCtor::DateCtor(Engine *engine) : Function(engine) { } RegExpCtor::RegExpCtor(Engine *engine) : Function(engine) { } const Value *ObjectCtor::invoke(const Activation *activation) const { ObjectValue *thisObject = activation->thisObject(); if (activation->calledAsFunction()) thisObject = engine()->newObject(); thisObject->setClassName("Object"); thisObject->setPrototype(engine()->objectPrototype()); thisObject->setProperty("length", engine()->numberValue()); return thisObject; } const Value *FunctionCtor::invoke(const Activation *activation) const { ObjectValue *thisObject = activation->thisObject(); if (activation->calledAsFunction()) thisObject = engine()->newObject(); thisObject->setClassName("Function"); thisObject->setPrototype(engine()->functionPrototype()); thisObject->setProperty("length", engine()->numberValue()); return thisObject; } const Value *ArrayCtor::invoke(const Activation *activation) const { ObjectValue *thisObject = activation->thisObject(); if (activation->calledAsFunction()) thisObject = engine()->newObject(); thisObject->setClassName("Array"); thisObject->setPrototype(engine()->arrayPrototype()); thisObject->setProperty("length", engine()->numberValue()); return thisObject; } const Value *StringCtor::invoke(const Activation *activation) const { if (activation->calledAsFunction()) return engine()->convertToString(activation->thisObject()); ObjectValue *thisObject = activation->thisObject(); thisObject->setClassName("String"); thisObject->setPrototype(engine()->stringPrototype()); thisObject->setProperty("length", engine()->numberValue()); return thisObject; } const Value *BooleanCtor::invoke(const Activation *activation) const { if (activation->calledAsFunction()) return engine()->convertToBoolean(activation->thisObject()); ObjectValue *thisObject = activation->thisObject(); thisObject->setClassName("Boolean"); thisObject->setPrototype(engine()->booleanPrototype()); return thisObject; } const Value *NumberCtor::invoke(const Activation *activation) const { if (activation->calledAsFunction()) return engine()->convertToNumber(activation->thisObject()); ObjectValue *thisObject = activation->thisObject(); thisObject->setClassName("Number"); thisObject->setPrototype(engine()->numberPrototype()); return thisObject; } const Value *DateCtor::invoke(const Activation *activation) const { if (activation->calledAsFunction()) return engine()->stringValue(); ObjectValue *thisObject = activation->thisObject(); thisObject->setClassName("Date"); thisObject->setPrototype(engine()->datePrototype()); return thisObject; } const Value *RegExpCtor::invoke(const Activation *activation) const { ObjectValue *thisObject = activation->thisObject(); if (activation->calledAsFunction()) thisObject = engine()->newObject(); thisObject->setClassName("RegExp"); thisObject->setPrototype(engine()->regexpPrototype()); thisObject->setProperty("source", engine()->stringValue()); thisObject->setProperty("global", engine()->booleanValue()); thisObject->setProperty("ignoreCase", engine()->booleanValue()); thisObject->setProperty("multiline", engine()->booleanValue()); thisObject->setProperty("lastIndex", engine()->numberValue()); return thisObject; } } // end of anonymous namespace //////////////////////////////////////////////////////////////////////////////// // ValueVisitor //////////////////////////////////////////////////////////////////////////////// ValueVisitor::ValueVisitor() { } ValueVisitor::~ValueVisitor() { } void ValueVisitor::visit(const NullValue *) { } void ValueVisitor::visit(const UndefinedValue *) { } 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 EasingCurveNameValue *) { } 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 NumberValue *Value::asNumberValue() const { return 0; } const BooleanValue *Value::asBooleanValue() const { return 0; } const StringValue *Value::asStringValue() 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 EasingCurveNameValue *Value::asEasingCurveNameValue() const { return 0; } const ColorValue *Value::asColorValue() const { return 0; } const AnchorLineValue *Value::asAnchorLineValue() 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 UndefinedValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const NumberValue *NumberValue::asNumberValue() 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; } void StringValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } ScopeChain::ScopeChain() : globalScope(0) , qmlTypes(0) { } ScopeChain::QmlComponentChain::QmlComponentChain() : rootObject(0), ids(0) { } ScopeChain::QmlComponentChain::~QmlComponentChain() { qDeleteAll(instantiatingComponents); } void ScopeChain::QmlComponentChain::clear() { qDeleteAll(instantiatingComponents); instantiatingComponents.clear(); rootObject = 0; functionScopes.clear(); ids = 0; } void ScopeChain::QmlComponentChain::add(QList<const ObjectValue *> *list) const { foreach (QmlComponentChain *parent, instantiatingComponents) parent->add(list); if (rootObject) list->append(rootObject); list->append(functionScopes); if (ids) list->append(ids); } void ScopeChain::update() { _all.clear(); _all += globalScope; foreach (QmlComponentChain *parent, qmlComponentScope.instantiatingComponents) parent->add(&_all); if (qmlComponentScope.rootObject && ! qmlScopeObjects.contains(qmlComponentScope.rootObject)) _all += qmlComponentScope.rootObject; _all += qmlScopeObjects; _all += qmlComponentScope.functionScopes; if (qmlComponentScope.ids) _all += qmlComponentScope.ids; if (qmlTypes) _all += qmlTypes; _all += jsScopes; } QList<const ObjectValue *> ScopeChain::all() const { return _all; } Context::Context(Engine *engine) : _engine(engine), _lookupMode(JSLookup), _qmlScopeObjectIndex(-1), _qmlScopeObjectSet(false) { } Context::~Context() { } void Context::build(const QList<Node *> &astPath, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot) { Link link(this, doc, snapshot); link.scopeChainAt(doc, astPath); } Engine *Context::engine() const { return _engine; } const ScopeChain &Context::scopeChain() const { return _scopeChain; } ScopeChain &Context::scopeChain() { return _scopeChain; } Context::LookupMode Context::lookupMode() const { return _lookupMode; } void Context::setLookupMode(LookupMode lookupMode) { _lookupMode = lookupMode; } const ObjectValue *Context::typeEnvironment(const QmlJS::Document *doc) const { return _typeEnvironments.value(doc, 0); } void Context::setTypeEnvironment(const QmlJS::Document *doc, const ObjectValue *typeEnvironment) { _typeEnvironments[doc] = typeEnvironment; } const Value *Context::lookup(const QString &name) { QList<const ObjectValue *> scopes = _scopeChain.all(); for (int index = scopes.size() - 1; index != -1; --index) { const ObjectValue *scope = scopes.at(index); if (const Value *member = scope->lookupMember(name, this)) { if (_lookupMode == JSLookup || ! dynamic_cast<const ASTVariableReference *>(member)) { return member; } } } return _engine->undefinedValue(); } const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId *qmlTypeName) { const ObjectValue *objectValue = typeEnvironment(doc); for (UiQualifiedId *iter = qmlTypeName; objectValue && iter; iter = iter->next) { if (! iter->name) return 0; const Value *value = objectValue->property(iter->name->asString(), this); if (!value) return 0; objectValue = value->asObjectValue(); } return objectValue; } const Value *Context::property(const ObjectValue *object, const QString &name) const { const Properties properties = _properties.value(object); return properties.value(name, engine()->undefinedValue()); } void Context::setProperty(const ObjectValue *object, const QString &name, const Value *value) { _properties[object].insert(name, value); } Reference::Reference(Engine *engine) : _engine(engine) { _engine->registerValue(this); } Reference::~Reference() { } Engine *Reference::engine() const { return _engine; } const Reference *Reference::asReference() const { return this; } void Reference::accept(ValueVisitor *visitor) const { visitor->visit(this); } const Value *Reference::value(Context *) const { return _engine->undefinedValue(); } void EasingCurveNameValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } QSet<QString> EasingCurveNameValue::_curveNames; QSet<QString> EasingCurveNameValue::curveNames() { if (_curveNames.isEmpty()) { _curveNames = QSet<QString>() << "easeLinear" << "easeInQuad" << "easeOutQuad" << "easeInOutQuad" << "easeOutInQuad" << "easeInCubic" << "easeOutCubic" << "easeInOutCubic" << "easeOutInCubic" << "easeInQuart" << "easeOutQuart" << "easeInOutQuart" << "easeOutInQuart" << "easeInQuint" << "easeOutQuint" << "easeInOutQuint" << "easeOutInQuint" << "easeInSine" << "easeOutSine" << "easeInOutSine" << "easeOutInSine" << "easeInExpo" << "easeOutExpo" << "easeInOutExpo" << "easeOutInExpo" << "easeInCirc" << "easeOutCirc" << "easeInOutCirc" << "easeOutInCirc" << "easeInElastic" << "easeOutElastic" << "easeInOutElastic" << "easeOutInElastic" << "easeInBack" << "easeOutBack" << "easeInOutBack" << "easeOutInBack" << "easeInBounce" << "easeOutBounce" << "easeInOutBounce" << "easeOutInBounce"; } return _curveNames; } const EasingCurveNameValue *EasingCurveNameValue::asEasingCurveNameValue() const { return this; } 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(Engine *engine) : _engine(engine), _prototype(0) { engine->registerValue(this); } ObjectValue::~ObjectValue() { } Engine *ObjectValue::engine() const { return _engine; } QString ObjectValue::className() const { return _className; } void ObjectValue::setClassName(const QString &className) { _className = className; } const ObjectValue *ObjectValue::prototype(Context *context) const { const ObjectValue *prototypeObject = value_cast<const ObjectValue *>(_prototype); if (! prototypeObject) { if (const Reference *prototypeReference = value_cast<const Reference *>(_prototype)) { prototypeObject = value_cast<const ObjectValue *>(prototypeReference->value(context)); } } return prototypeObject; } void ObjectValue::setPrototype(const Value *prototype) { // ### FIXME: Check for cycles. _prototype = prototype; } void ObjectValue::setProperty(const QString &name, const Value *value) { _members[name] = value; } void ObjectValue::removeProperty(const QString &name) { _members.remove(name); } const ObjectValue *ObjectValue::asObjectValue() const { return this; } void ObjectValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } const Value *ObjectValue::property(const QString &name, Context *context) const { return lookupMember(name, context); } 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, Context *context) const { if (const Value *m = _members.value(name)) return m; else { LookupMember slowLookup(name); processMembers(&slowLookup); if (slowLookup.value()) return slowLookup.value(); } const ObjectValue *prototypeObject = prototype(context); if (prototypeObject) { if (const Value *m = prototypeObject->lookupMember(name, context)) return m; } return 0; } Activation::Activation(Context *parentContext) : _thisObject(0), _calledAsFunction(true), _parentContext(parentContext) { } Activation::~Activation() { } Context *Activation::parentContext() const { return _parentContext; } Context *Activation::context() const { // ### FIXME: Real context for activations. return 0; } bool Activation::calledAsConstructor() const { return ! _calledAsFunction; } void Activation::setCalledAsConstructor(bool calledAsConstructor) { _calledAsFunction = ! calledAsConstructor; } bool Activation::calledAsFunction() const { return _calledAsFunction; } void Activation::setCalledAsFunction(bool calledAsFunction) { _calledAsFunction = calledAsFunction; } ObjectValue *Activation::thisObject() const { return _thisObject; } void Activation::setThisObject(ObjectValue *thisObject) { _thisObject = thisObject; } ValueList Activation::arguments() const { return _arguments; } void Activation::setArguments(const ValueList &arguments) { _arguments = arguments; } FunctionValue::FunctionValue(Engine *engine) : ObjectValue(engine) { } FunctionValue::~FunctionValue() { } const Value *FunctionValue::construct(const ValueList &actuals) const { Activation activation; activation.setCalledAsConstructor(true); activation.setThisObject(engine()->newObject()); activation.setArguments(actuals); return invoke(&activation); } const Value *FunctionValue::call(const ValueList &actuals) const { Activation activation; activation.setCalledAsFunction(true); activation.setThisObject(engine()->globalObject()); // ### FIXME: it should be `null' activation.setArguments(actuals); return invoke(&activation); } const Value *FunctionValue::call(const ObjectValue *thisObject, const ValueList &actuals) const { Activation activation; activation.setCalledAsFunction(true); activation.setThisObject(const_cast<ObjectValue *>(thisObject)); // ### FIXME: remove the const_cast activation.setArguments(actuals); return invoke(&activation); } const Value *FunctionValue::returnValue() const { return engine()->undefinedValue(); } int FunctionValue::argumentCount() const { return 0; } const Value *FunctionValue::argument(int) const { return engine()->undefinedValue(); } QString FunctionValue::argumentName(int index) const { return QString::fromLatin1("arg%1").arg(index + 1); } bool FunctionValue::isVariadic() const { return true; } const Value *FunctionValue::invoke(const Activation *activation) const { return activation->thisObject(); // ### FIXME: it should return undefined } const FunctionValue *FunctionValue::asFunctionValue() const { return this; } void FunctionValue::accept(ValueVisitor *visitor) const { visitor->visit(this); } Function::Function(Engine *engine) : FunctionValue(engine), _returnValue(0) { setClassName("Function"); } Function::~Function() { } void Function::addArgument(const Value *argument) { _arguments.push_back(argument); } const Value *Function::returnValue() const { return _returnValue; } void Function::setReturnValue(const Value *returnValue) { _returnValue = returnValue; } int Function::argumentCount() const { return _arguments.size(); } const Value *Function::argument(int index) const { return _arguments.at(index); } const Value *Function::property(const QString &name, Context *context) const { if (name == "length") return engine()->numberValue(); return FunctionValue::property(name, context); } const Value *Function::invoke(const Activation *activation) const { return activation->thisObject(); // ### FIXME it should return undefined } //////////////////////////////////////////////////////////////////////////////// // typing environment //////////////////////////////////////////////////////////////////////////////// ConvertToNumber::ConvertToNumber(Engine *engine) : _engine(engine), _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 = _engine->numberValue(); } void ConvertToNumber::visit(const UndefinedValue *) { _result = _engine->numberValue(); } void ConvertToNumber::visit(const NumberValue *value) { _result = value; } void ConvertToNumber::visit(const BooleanValue *) { _result = _engine->numberValue(); } void ConvertToNumber::visit(const StringValue *) { _result = _engine->numberValue(); } void ConvertToNumber::visit(const ObjectValue *object) { if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) { _result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number? } } void ConvertToNumber::visit(const FunctionValue *object) { if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) { _result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number? } } ConvertToString::ConvertToString(Engine *engine) : _engine(engine), _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 = _engine->stringValue(); } void ConvertToString::visit(const UndefinedValue *) { _result = _engine->stringValue(); } void ConvertToString::visit(const NumberValue *) { _result = _engine->stringValue(); } void ConvertToString::visit(const BooleanValue *) { _result = _engine->stringValue(); } void ConvertToString::visit(const StringValue *value) { _result = value; } void ConvertToString::visit(const ObjectValue *object) { if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) { _result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string? } } void ConvertToString::visit(const FunctionValue *object) { if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) { _result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string? } } ConvertToObject::ConvertToObject(Engine *engine) : _engine(engine), _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 = _engine->nullValue(); } void ConvertToObject::visit(const NumberValue *value) { ValueList actuals; actuals.append(value); _result = _engine->numberCtor()->construct(actuals); } void ConvertToObject::visit(const BooleanValue *value) { ValueList actuals; actuals.append(value); _result = _engine->booleanCtor()->construct(actuals); } void ConvertToObject::visit(const StringValue *value) { ValueList actuals; actuals.append(value); _result = _engine->stringCtor()->construct(actuals); } 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 EasingCurveNameValue *) { _result = QLatin1String("string"); } void TypeId::visit(const ColorValue *) { _result = QLatin1String("string"); } void TypeId::visit(const AnchorLineValue *) { _result = QLatin1String("AnchorLine"); } Engine::Engine() : _objectPrototype(0), _functionPrototype(0), _numberPrototype(0), _booleanPrototype(0), _stringPrototype(0), _arrayPrototype(0), _datePrototype(0), _regexpPrototype(0), _objectCtor(0), _functionCtor(0), _arrayCtor(0), _stringCtor(0), _booleanCtor(0), _numberCtor(0), _dateCtor(0), _regexpCtor(0), _globalObject(0), _mathObject(0), _qtObject(0), #ifndef NO_DECLARATIVE_BACKEND _qmlKeysObject(0), #endif _convertToNumber(this), _convertToString(this), _convertToObject(this) { initializePrototypes(); _metaTypeSystem.reload(this); } Engine::~Engine() { qDeleteAll(_registeredValues); } const NullValue *Engine::nullValue() const { return &_nullValue; } const UndefinedValue *Engine::undefinedValue() const { return &_undefinedValue; } const NumberValue *Engine::numberValue() const { return &_numberValue; } const BooleanValue *Engine::booleanValue() const { return &_booleanValue; } const StringValue *Engine::stringValue() const { return &_stringValue; } const EasingCurveNameValue *Engine::easingCurveNameValue() const { return &_easingCurveNameValue; } const ColorValue *Engine::colorValue() const { return &_colorValue; } const AnchorLineValue *Engine::anchorLineValue() const { return &_anchorLineValue; } const Value *Engine::newArray() { return arrayCtor()->construct(); } ObjectValue *Engine::newObject() { return newObject(_objectPrototype); } ObjectValue *Engine::newObject(const ObjectValue *prototype) { ObjectValue *object = new ObjectValue(this); object->setPrototype(prototype); return object; } Function *Engine::newFunction() { Function *function = new Function(this); function->setPrototype(functionPrototype()); return function; } ObjectValue *Engine::globalObject() const { return _globalObject; } ObjectValue *Engine::objectPrototype() const { return _objectPrototype; } ObjectValue *Engine::functionPrototype() const { return _functionPrototype; } ObjectValue *Engine::numberPrototype() const { return _numberPrototype; } ObjectValue *Engine::booleanPrototype() const { return _booleanPrototype; } ObjectValue *Engine::stringPrototype() const { return _stringPrototype; } ObjectValue *Engine::arrayPrototype() const { return _arrayPrototype; } ObjectValue *Engine::datePrototype() const { return _datePrototype; } ObjectValue *Engine::regexpPrototype() const { return _regexpPrototype; } const FunctionValue *Engine::objectCtor() const { return _objectCtor; } const FunctionValue *Engine::functionCtor() const { return _functionCtor; } const FunctionValue *Engine::arrayCtor() const { return _arrayCtor; } const FunctionValue *Engine::stringCtor() const { return _stringCtor; } const FunctionValue *Engine::booleanCtor() const { return _booleanCtor; } const FunctionValue *Engine::numberCtor() const { return _numberCtor; } const FunctionValue *Engine::dateCtor() const { return _dateCtor; } const FunctionValue *Engine::regexpCtor() const { return _regexpCtor; } const ObjectValue *Engine::mathObject() const { return _mathObject; } const ObjectValue *Engine::qtObject() const { return _qtObject; } void Engine::registerValue(Value *value) { _registeredValues.append(value); } const Value *Engine::convertToBoolean(const Value *value) { return _convertToNumber(value); // ### implement convert to bool } const Value *Engine::convertToNumber(const Value *value) { return _convertToNumber(value); } const Value *Engine::convertToString(const Value *value) { return _convertToString(value); } const Value *Engine::convertToObject(const Value *value) { return _convertToObject(value); } QString Engine::typeId(const Value *value) { return _typeId(value); } void Engine::addFunction(ObjectValue *object, const QString &name, const Value *result, int argumentCount) { Function *function = newFunction(); function->setReturnValue(result); for (int i = 0; i < argumentCount; ++i) function->addArgument(undefinedValue()); // ### introduce unknownValue object->setProperty(name, function); } void Engine::addFunction(ObjectValue *object, const QString &name, int argumentCount) { Function *function = newFunction(); for (int i = 0; i < argumentCount; ++i) function->addArgument(undefinedValue()); // ### introduce unknownValue object->setProperty(name, function); } void Engine::initializePrototypes() { _objectPrototype = newObject(/*prototype = */ 0); _functionPrototype = newObject(_objectPrototype); _numberPrototype = newObject(_objectPrototype); _booleanPrototype = newObject(_objectPrototype); _stringPrototype = newObject(_objectPrototype); _arrayPrototype = newObject(_objectPrototype); _datePrototype = newObject(_objectPrototype); _regexpPrototype = newObject(_objectPrototype); // set up the Global object _globalObject = newObject(); _globalObject->setClassName("Global"); // set up the default Object prototype _objectCtor = new ObjectCtor(this); _objectCtor->setPrototype(_functionPrototype); _objectCtor->setProperty("prototype", _objectPrototype); _functionCtor = new FunctionCtor(this); _functionCtor->setPrototype(_functionPrototype); _functionCtor->setProperty("prototype", _functionPrototype); _arrayCtor = new ArrayCtor(this); _arrayCtor->setPrototype(_functionPrototype); _arrayCtor->setProperty("prototype", _arrayPrototype); _stringCtor = new StringCtor(this); _stringCtor->setPrototype(_functionPrototype); _stringCtor->setProperty("prototype", _stringPrototype); _booleanCtor = new BooleanCtor(this); _booleanCtor->setPrototype(_functionPrototype); _booleanCtor->setProperty("prototype", _booleanPrototype); _numberCtor = new NumberCtor(this); _numberCtor->setPrototype(_functionPrototype); _numberCtor->setProperty("prototype", _numberPrototype); _dateCtor = new DateCtor(this); _dateCtor->setPrototype(_functionPrototype); _dateCtor->setProperty("prototype", _datePrototype); _regexpCtor = new RegExpCtor(this); _regexpCtor->setPrototype(_functionPrototype); _regexpCtor->setProperty("prototype", _regexpPrototype); addFunction(_objectCtor, "getPrototypeOf", 1); addFunction(_objectCtor, "getOwnPropertyDescriptor", 2); addFunction(_objectCtor, "getOwnPropertyNames", newArray(), 1); addFunction(_objectCtor, "create", 1); addFunction(_objectCtor, "defineProperty", 3); addFunction(_objectCtor, "defineProperties", 2); addFunction(_objectCtor, "seal", 1); addFunction(_objectCtor, "freeze", 1); addFunction(_objectCtor, "preventExtensions", 1); addFunction(_objectCtor, "isSealed", booleanValue(), 1); addFunction(_objectCtor, "isFrozen", booleanValue(), 1); addFunction(_objectCtor, "isExtensible", booleanValue(), 1); addFunction(_objectCtor, "keys", newArray(), 1); addFunction(_objectPrototype, "toString", stringValue(), 0); addFunction(_objectPrototype, "toLocaleString", stringValue(), 0); addFunction(_objectPrototype, "valueOf", 0); // ### FIXME it should return thisObject addFunction(_objectPrototype, "hasOwnProperty", booleanValue(), 1); addFunction(_objectPrototype, "isPrototypeOf", booleanValue(), 1); addFunction(_objectPrototype, "propertyIsEnumerable", booleanValue(), 1); // set up the default Function prototype _functionPrototype->setProperty("constructor", _functionCtor); addFunction(_functionPrototype, "toString", stringValue(), 0); addFunction(_functionPrototype, "apply", 2); addFunction(_functionPrototype, "call", 1); addFunction(_functionPrototype, "bind", 1); // set up the default Array prototype addFunction(_arrayCtor, "isArray", booleanValue(), 1); _arrayPrototype->setProperty("constructor", _arrayCtor); addFunction(_arrayPrototype, "toString", stringValue(), 0); addFunction(_arrayPrototype, "toLocalString", stringValue(), 0); addFunction(_arrayPrototype, "concat", 0); addFunction(_arrayPrototype, "join", 1); addFunction(_arrayPrototype, "pop", 0); addFunction(_arrayPrototype, "push", 0); addFunction(_arrayPrototype, "reverse", 0); addFunction(_arrayPrototype, "shift", 0); addFunction(_arrayPrototype, "slice", 2); addFunction(_arrayPrototype, "sort", 1); addFunction(_arrayPrototype, "splice", 2); addFunction(_arrayPrototype, "unshift", 0); addFunction(_arrayPrototype, "indexOf", numberValue(), 1); addFunction(_arrayPrototype, "lastIndexOf", numberValue(), 1); addFunction(_arrayPrototype, "every", 1); addFunction(_arrayPrototype, "some", 1); addFunction(_arrayPrototype, "forEach", 1); addFunction(_arrayPrototype, "map", 1); addFunction(_arrayPrototype, "filter", 1); addFunction(_arrayPrototype, "reduce", 1); addFunction(_arrayPrototype, "reduceRight", 1); // set up the default String prototype addFunction(_stringCtor, "fromCharCode", stringValue(), 0); _stringPrototype->setProperty("constructor", _stringCtor); addFunction(_stringPrototype, "toString", stringValue(), 0); addFunction(_stringPrototype, "valueOf", stringValue(), 0); addFunction(_stringPrototype, "charAt", stringValue(), 1); addFunction(_stringPrototype, "charCodeAt", stringValue(), 1); addFunction(_stringPrototype, "concat", stringValue(), 0); addFunction(_stringPrototype, "indexOf", numberValue(), 2); addFunction(_stringPrototype, "lastIndexOf", numberValue(), 2); addFunction(_stringPrototype, "localeCompare", booleanValue(), 1); addFunction(_stringPrototype, "match", newArray(), 1); addFunction(_stringPrototype, "replace", stringValue(), 2); addFunction(_stringPrototype, "search", numberValue(), 1); addFunction(_stringPrototype, "slice", stringValue(), 2); addFunction(_stringPrototype, "split", newArray(), 1); addFunction(_stringPrototype, "substring", stringValue(), 2); addFunction(_stringPrototype, "toLowerCase", stringValue(), 0); addFunction(_stringPrototype, "toLocaleLowerCase", stringValue(), 0); addFunction(_stringPrototype, "toUpperCase", stringValue(), 0); addFunction(_stringPrototype, "toLocaleUpperCase", stringValue(), 0); addFunction(_stringPrototype, "trim", stringValue(), 0); // set up the default Boolean prototype addFunction(_booleanCtor, "fromCharCode", 0); _booleanPrototype->setProperty("constructor", _booleanCtor); addFunction(_booleanPrototype, "toString", stringValue(), 0); addFunction(_booleanPrototype, "valueOf", booleanValue(), 0); // set up the default Number prototype _numberCtor->setProperty("MAX_VALUE", numberValue()); _numberCtor->setProperty("MIN_VALUE", numberValue()); _numberCtor->setProperty("NaN", numberValue()); _numberCtor->setProperty("NEGATIVE_INFINITY", numberValue()); _numberCtor->setProperty("POSITIVE_INFINITY", numberValue()); addFunction(_numberCtor, "fromCharCode", 0); _numberPrototype->setProperty("constructor", _numberCtor); addFunction(_numberPrototype, "toString", stringValue(), 0); addFunction(_numberPrototype, "toLocaleString", stringValue(), 0); addFunction(_numberPrototype, "valueOf", numberValue(), 0); addFunction(_numberPrototype, "toFixed", numberValue(), 1); addFunction(_numberPrototype, "toExponential", numberValue(), 1); addFunction(_numberPrototype, "toPrecision", numberValue(), 1); // set up the Math object _mathObject = newObject(); _mathObject->setProperty("E", numberValue()); _mathObject->setProperty("LN10", numberValue()); _mathObject->setProperty("LN2", numberValue()); _mathObject->setProperty("LOG2E", numberValue()); _mathObject->setProperty("LOG10E", numberValue()); _mathObject->setProperty("PI", numberValue()); _mathObject->setProperty("SQRT1_2", numberValue()); _mathObject->setProperty("SQRT2", numberValue()); addFunction(_mathObject, "abs", numberValue(), 1); addFunction(_mathObject, "acos", numberValue(), 1); addFunction(_mathObject, "asin", numberValue(), 1); addFunction(_mathObject, "atan", numberValue(), 1); addFunction(_mathObject, "atan2", numberValue(), 2); addFunction(_mathObject, "ceil", numberValue(), 1); addFunction(_mathObject, "cos", numberValue(), 1); addFunction(_mathObject, "exp", numberValue(), 1); addFunction(_mathObject, "floor", numberValue(), 1); addFunction(_mathObject, "log", numberValue(), 1); addFunction(_mathObject, "max", numberValue(), 0); addFunction(_mathObject, "min", numberValue(), 0); addFunction(_mathObject, "pow", numberValue(), 2); addFunction(_mathObject, "random", numberValue(), 1); addFunction(_mathObject, "round", numberValue(), 1); addFunction(_mathObject, "sin", numberValue(), 1); addFunction(_mathObject, "sqrt", numberValue(), 1); addFunction(_mathObject, "tan", numberValue(), 1); // set up the default Boolean prototype addFunction(_dateCtor, "parse", numberValue(), 1); addFunction(_dateCtor, "now", numberValue(), 0); _datePrototype->setProperty("constructor", _dateCtor); addFunction(_datePrototype, "toString", stringValue(), 0); addFunction(_datePrototype, "toDateString", stringValue(), 0); addFunction(_datePrototype, "toTimeString", stringValue(), 0); addFunction(_datePrototype, "toLocaleString", stringValue(), 0); addFunction(_datePrototype, "toLocaleDateString", stringValue(), 0); addFunction(_datePrototype, "toLocaleTimeString", stringValue(), 0); addFunction(_datePrototype, "valueOf", numberValue(), 0); addFunction(_datePrototype, "getTime", numberValue(), 0); addFunction(_datePrototype, "getFullYear", numberValue(), 0); addFunction(_datePrototype, "getUTCFullYear", numberValue(), 0); addFunction(_datePrototype, "getMonth", numberValue(), 0); addFunction(_datePrototype, "getUTCMonth", numberValue(), 0); addFunction(_datePrototype, "getDate", numberValue(), 0); addFunction(_datePrototype, "getUTCDate", numberValue(), 0); addFunction(_datePrototype, "getHours", numberValue(), 0); addFunction(_datePrototype, "getUTCHours", numberValue(), 0); addFunction(_datePrototype, "getMinutes", numberValue(), 0); addFunction(_datePrototype, "getUTCMinutes", numberValue(), 0); addFunction(_datePrototype, "getSeconds", numberValue(), 0); addFunction(_datePrototype, "getUTCSeconds", numberValue(), 0); addFunction(_datePrototype, "getMilliseconds", numberValue(), 0); addFunction(_datePrototype, "getUTCMilliseconds", numberValue(), 0); addFunction(_datePrototype, "getTimezoneOffset", numberValue(), 0); addFunction(_datePrototype, "setTime", 1); addFunction(_datePrototype, "setMilliseconds", 1); addFunction(_datePrototype, "setUTCMilliseconds", 1); addFunction(_datePrototype, "setSeconds", 1); addFunction(_datePrototype, "setUTCSeconds", 1); addFunction(_datePrototype, "setMinutes", 1); addFunction(_datePrototype, "setUTCMinutes", 1); addFunction(_datePrototype, "setHours", 1); addFunction(_datePrototype, "setUTCHours", 1); addFunction(_datePrototype, "setDate", 1); addFunction(_datePrototype, "setUTCDate", 1); addFunction(_datePrototype, "setMonth", 1); addFunction(_datePrototype, "setUTCMonth", 1); addFunction(_datePrototype, "setFullYear", 1); addFunction(_datePrototype, "setUTCFullYear", 1); addFunction(_datePrototype, "toUTCString", stringValue(), 0); addFunction(_datePrototype, "toISOString", stringValue(), 0); addFunction(_datePrototype, "toJSON", stringValue(), 1); // set up the default Boolean prototype _regexpPrototype->setProperty("constructor", _regexpCtor); addFunction(_regexpPrototype, "exec", newArray(), 1); addFunction(_regexpPrototype, "test", booleanValue(), 1); addFunction(_regexpPrototype, "toString", stringValue(), 0); // fill the Global object _globalObject->setProperty("Math", _mathObject); _globalObject->setProperty("Object", objectCtor()); _globalObject->setProperty("Function", functionCtor()); _globalObject->setProperty("Array", arrayCtor()); _globalObject->setProperty("String", stringCtor()); _globalObject->setProperty("Boolean", booleanCtor()); _globalObject->setProperty("Number", numberCtor()); _globalObject->setProperty("Date", dateCtor()); _globalObject->setProperty("RegExp", regexpCtor()); //types _qtObject = newObject(/*prototype */ 0); addFunction(_qtObject, QLatin1String("rgba"), 4); addFunction(_qtObject, QLatin1String("hsla"), 4); addFunction(_qtObject, QLatin1String("rect"), 4); addFunction(_qtObject, QLatin1String("point"), 2); addFunction(_qtObject, QLatin1String("size"), 2); addFunction(_qtObject, QLatin1String("vector3d"), 3); //color helpers addFunction(_qtObject, QLatin1String("lighter"), 1); addFunction(_qtObject, QLatin1String("darker"), 1); addFunction(_qtObject, QLatin1String("tint"), 2); //misc methods addFunction(_qtObject, QLatin1String("closestAngle"), 2); addFunction(_qtObject, QLatin1String("playSound"), 1); addFunction(_qtObject, QLatin1String("openUrlExternally"), 1); addFunction(_qtObject, QLatin1String("md5"), 1); addFunction(_qtObject, QLatin1String("btoa"), 1); addFunction(_qtObject, QLatin1String("atob"), 1); addFunction(_qtObject, QLatin1String("quit"), 0); addFunction(_qtObject, QLatin1String("resolvedUrl"), 1); //firebug/webkit compat ObjectValue *consoleObject = newObject(/*prototype */ 0); addFunction(consoleObject, QLatin1String("log"), 1); addFunction(consoleObject, QLatin1String("debug"), 1); _globalObject->setProperty(QLatin1String("console"), consoleObject); _globalObject->setProperty(QLatin1String("Qt"), _qtObject); } const ObjectValue *Engine::qmlKeysObject() { #ifndef NO_DECLARATIVE_BACKEND return _qmlKeysObject; #else return 0; #endif } const Value *Engine::defaultValueForBuiltinType(const QString &typeName) const { if (typeName == QLatin1String("string") || typeName == QLatin1String("url")) return stringValue(); else if (typeName == QLatin1String("bool")) return booleanValue(); else if (typeName == QLatin1String("int") || typeName == QLatin1String("real")) return numberValue(); // ### more types... return undefinedValue(); } #ifndef NO_DECLARATIVE_BACKEND QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix, int majorVersion, int minorVersion) { if (name == QLatin1String("QDeclarativeAnchors")) { QmlObjectValue *object = new QmlObjectValue(&QDeclarativeAnchors::staticMetaObject, QLatin1String("Anchors"), -1, -1, this); return object; } else if (name == QLatin1String("QDeclarativePen")) { QmlObjectValue *object = new QmlObjectValue(&QDeclarativePen::staticMetaObject, QLatin1String("Pen"), -1, -1, this); return object; } else if (name == QLatin1String("QDeclarativeScaleGrid")) { QmlObjectValue *object = new QmlObjectValue(&QObject::staticMetaObject, QLatin1String("ScaleGrid"), -1, -1, this); object->setProperty("left", numberValue()); object->setProperty("top", numberValue()); object->setProperty("right", numberValue()); object->setProperty("bottom", numberValue()); return object; } // ### TODO: add support for QML packages const QString componentName = prefix + QLatin1Char('/') + name; if (QDeclarativeType *qmlType = QDeclarativeMetaType::qmlType(componentName.toUtf8(), majorVersion, minorVersion)) { const QString typeName = qmlType->qmlTypeName(); const QString strippedTypeName = typeName.mid(typeName.lastIndexOf('/') + 1); QmlObjectValue *object = new QmlObjectValue(qmlType->metaObject(), strippedTypeName, majorVersion, minorVersion, this); return object; } return 0; } #endif ASTObjectValue::ASTObjectValue(UiQualifiedId *typeName, UiObjectInitializer *initializer, const QmlJS::Document *doc, Engine *engine) : ObjectValue(engine), _typeName(typeName), _initializer(initializer), _doc(doc) { 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 && def->memberType) { ASTPropertyReference *ref = new ASTPropertyReference(def, _doc, engine); _properties.append(ref); } else if (def->type == UiPublicMember::Signal && def->name) { ASTSignalReference *ref = new ASTSignalReference(def, _doc, engine); _signals.append(ref); } } } } } ASTObjectValue::~ASTObjectValue() { } 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->asString(), ref); // ### Should get a different value? processor->processGeneratedSlot(ref->onChangedSlotName(), ref); } foreach (ASTSignalReference *ref, _signals) { processor->processSignal(ref->ast()->name->asString(), ref); // ### Should get a different value? processor->processGeneratedSlot(ref->slotName(), ref); } ObjectValue::processMembers(processor); } ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, Engine *engine) : Reference(engine), _ast(ast) { } ASTVariableReference::~ASTVariableReference() { } const Value *ASTVariableReference::value(Context *context) const { Evaluate 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; } QmlPrototypeReference::QmlPrototypeReference(UiQualifiedId *qmlTypeName, const QmlJS::Document *doc, Engine *engine) : Reference(engine), _qmlTypeName(qmlTypeName), _doc(doc) { } QmlPrototypeReference::~QmlPrototypeReference() { } UiQualifiedId *QmlPrototypeReference::qmlTypeName() const { return _qmlTypeName; } const Value *QmlPrototypeReference::value(Context *context) const { return context->lookupType(_doc, _qmlTypeName); } ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const QmlJS::Document *doc, Engine *engine) : Reference(engine), _ast(ast), _doc(doc) { const QString propertyName = ast->name->asString(); _onChangedSlotName = QLatin1String("on"); _onChangedSlotName += propertyName.at(0).toUpper(); _onChangedSlotName += propertyName.midRef(1); _onChangedSlotName += QLatin1String("Changed"); } ASTPropertyReference::~ASTPropertyReference() { } 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(Context *context) const { if (_ast->expression) { Evaluate check(context); return check(_ast->expression); } if (_ast->memberType) return engine()->defaultValueForBuiltinType(_ast->memberType->asString()); return engine()->undefinedValue(); } ASTSignalReference::ASTSignalReference(UiPublicMember *ast, const QmlJS::Document *doc, Engine *engine) : Reference(engine), _ast(ast), _doc(doc) { const QString signalName = ast->name->asString(); _slotName = QLatin1String("on"); _slotName += signalName.at(0).toUpper(); _slotName += signalName.midRef(1); } ASTSignalReference::~ASTSignalReference() { } bool ASTSignalReference::getSourceLocation(QString *fileName, int *line, int *column) const { *fileName = _doc->fileName(); *line = _ast->identifierToken.startLine; *column = _ast->identifierToken.startColumn; return true; } const Value *ASTSignalReference::value(Context *) const { return engine()->undefinedValue(); }