diff options
Diffstat (limited to 'src/qml/compiler/qqmlcodegenerator.cpp')
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 544 |
1 files changed, 356 insertions, 188 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 13b23fde68..f933465100 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -54,6 +54,8 @@ QT_USE_NAMESPACE +static const quint32 emptyStringIndex = 0; + DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); using namespace QtQml; @@ -64,6 +66,22 @@ using namespace QtQml; return false; \ } +void QmlObject::init(MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &loc) +{ + inheritedTypeNameIndex = typeNameIndex; + + location.line = loc.startLine; + location.column = loc.startColumn; + + idIndex = id; + indexOfDefaultProperty = -1; + properties = pool->New<PoolList<QmlProperty> >(); + qmlSignals = pool->New<PoolList<Signal> >(); + bindings = pool->New<PoolList<Binding> >(); + functions = pool->New<PoolList<Function> >(); + declarationsOverride = 0; +} + void QmlObject::dump(DebugStream &out) { out << inheritedTypeNameIndex << " {" << endl; @@ -73,6 +91,95 @@ void QmlObject::dump(DebugStream &out) out << "}" << endl; } +QString QmlObject::sanityCheckFunctionNames(const QList<CompiledFunctionOrExpression> &allFunctions, const QSet<QString> &illegalNames, AST::SourceLocation *errorLocation) +{ + QSet<int> functionNames; + for (Function *f = functions->first; f; f = f->next) { + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(allFunctions.at(f->index).node); + Q_ASSERT(function); + *errorLocation = function->identifierToken; + QString name = function->name.toString(); + if (functionNames.contains(f->nameIndex)) + return tr("Duplicate method name"); + functionNames.insert(f->nameIndex); + if (signalNames.contains(f->nameIndex)) + return tr("Duplicate method name"); + + if (name.at(0).isUpper()) + return tr("Method names cannot begin with an upper case letter"); + if (illegalNames.contains(name)) + return tr("Illegal method name"); + } + return QString(); // no error +} + +QString QmlObject::appendSignal(Signal *signal) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + if (target->signalNames.contains(signal->nameIndex)) + return tr("Duplicate signal name"); + target->signalNames.insert(signal->nameIndex); + target->qmlSignals->append(signal); + return QString(); // no error +} + +QString QmlObject::appendProperty(QmlProperty *prop, const QString &propertyName, bool isDefaultProperty, const AST::SourceLocation &defaultToken, AST::SourceLocation *errorLocation) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + + if (target->propertyNames.contains(prop->nameIndex)) + return tr("Duplicate property name"); + + if (propertyName.constData()->isUpper()) + return tr("Property names cannot begin with an upper case letter"); + + target->propertyNames.insert(prop->nameIndex); + + const int index = target->properties->append(prop); + if (isDefaultProperty) { + if (target->indexOfDefaultProperty != -1) { + *errorLocation = defaultToken; + return tr("Duplicate default property"); + } + target->indexOfDefaultProperty = index; + } + return QString(); // no error +} + +void QmlObject::appendFunction(Function *f) +{ + QmlObject *target = declarationsOverride; + if (!target) + target = this; + target->functions->append(f); +} + +QString QmlObject::appendBinding(Binding *b, bool isListBinding) +{ + const bool bindingToDefaultProperty = (b->propertyNameIndex == 0); + if (!isListBinding && !bindingToDefaultProperty + && b->type != QV4::CompiledData::Binding::Type_GroupProperty + && b->type != QV4::CompiledData::Binding::Type_AttachedProperty + && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + if (bindingNames.contains(b->propertyNameIndex)) + return tr("Property value set multiple times"); + bindingNames.insert(b->propertyNameIndex); + } + if (isListBinding) { + bindings->append(b); + } else if (bindingToDefaultProperty) { + Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::location>(b); + bindings->insertAfter(insertionPoint, b); + } else { + bindings->prepend(b); + } + return QString(); // no error +} + QStringList Signal::parameterStringList(const QStringList &stringPool) const { QStringList result; @@ -82,8 +189,10 @@ QStringList Signal::parameterStringList(const QStringList &stringPool) const return result; } -QQmlCodeGenerator::QQmlCodeGenerator() - : _object(0) +QQmlCodeGenerator::QQmlCodeGenerator(const QSet<QString> &illegalNames) + : illegalNames(illegalNames) + , _object(0) + , _propertyDeclaration(0) , jsGenerator(0) { } @@ -132,7 +241,7 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co this->pool = output->jsParserEngine.pool(); this->jsGenerator = &output->jsGenerator; - emptyStringIndex = registerString(QString()); + Q_ASSERT(registerString(QString()) == emptyStringIndex); sourceCode = code; @@ -205,7 +314,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) int idx = defineQMLObject(node); appendBinding(node->qualifiedTypeNameId->identifierToken, emptyStringIndex, idx); } else { - int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer); + int idx = defineQMLObject(/*qualfied type name id*/0, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object); appendBinding(node->qualifiedTypeNameId, idx); } return false; @@ -213,8 +322,8 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) { - int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); - appendBinding(node->qualifiedId, idx); + int idx = defineQMLObject(node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer); + appendBinding(node->qualifiedId, idx, node->hasOnToken); return false; } @@ -277,63 +386,31 @@ void QQmlCodeGenerator::accept(AST::Node *node) AST::Node::acceptChild(node, this); } -bool QQmlCodeGenerator::sanityCheckFunctionNames() -{ - QSet<QString> functionNames; - for (Function *f = _object->functions->first; f; f = f->next) { - AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index).node); - Q_ASSERT(function); - QString name = function->name.toString(); - if (functionNames.contains(name)) - COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name")); - functionNames.insert(name); - if (_signalNames.contains(name)) - COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name")); - - if (name.at(0).isUpper()) - COMPILE_EXCEPTION(function->identifierToken, tr("Method names cannot begin with an upper case letter")); -#if 0 // ### - if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name.toString())) - COMPILE_EXCEPTION(&currSlot, tr("Illegal method name")); -#endif - } - return true; -} - -int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer) +int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const AST::SourceLocation &location, AST::UiObjectInitializer *initializer, QmlObject *declarationsOverride) { QmlObject *obj = New<QmlObject>(); _objects.append(obj); const int objectIndex = _objects.size() - 1; qSwap(_object, obj); - _object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId)); + _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); + _object->declarationsOverride = declarationsOverride; - AST::SourceLocation loc; - if (qualifiedTypeNameId) - loc = qualifiedTypeNameId->firstSourceLocation(); - _object->location.line = loc.startLine; - _object->location.column = loc.startColumn; - - _object->idIndex = emptyStringIndex; - _object->indexOfDefaultProperty = -1; - _object->properties = New<PoolList<QmlProperty> >(); - _object->qmlSignals = New<PoolList<Signal> >(); - _object->bindings = New<PoolList<Binding> >(); - _object->functions = New<PoolList<Function> >(); - - QSet<QString> propertyNames; - qSwap(_propertyNames, propertyNames); - QSet<QString> signalNames; - qSwap(_signalNames, signalNames); + // A new object is also a boundary for property declarations. + QmlProperty *declaration = 0; + qSwap(_propertyDeclaration, declaration); accept(initializer); - sanityCheckFunctionNames(); + qSwap(_propertyDeclaration, declaration); - qSwap(_propertyNames, propertyNames); - qSwap(_signalNames, signalNames); qSwap(_object, obj); + + AST::SourceLocation loc; + QString error = obj->sanityCheckFunctionNames(_functions, illegalNames, &loc); + if (!error.isEmpty()) + recordError(loc, error); + return objectIndex; } @@ -522,7 +599,7 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) QString signalName = node->name.toString(); signal->nameIndex = registerString(signalName); - AST::SourceLocation loc = node->firstSourceLocation(); + AST::SourceLocation loc = node->typeToken; signal->location.line = loc.startLine; signal->location.column = loc.startColumn; @@ -582,19 +659,17 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) p = p->next; } - if (_signalNames.contains(signalName)) - COMPILE_EXCEPTION(node->identifierToken, tr("Duplicate signal name")); - _signalNames.insert(signalName); - if (signalName.at(0).isUpper()) COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter")); -#if 0 // ### cannot access identifier table from separate thread - if (enginePrivate->v8engine()->illegalNames().contains(currSig.name.toString())) - COMPILE_EXCEPTION(&currSig, tr("Illegal signal name")); -#endif + if (illegalNames.contains(signalName)) + COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name")); - _object->qmlSignals->append(signal); + QString error = _object->appendSignal(signal); + if (!error.isEmpty()) { + recordError(node->identifierToken, error); + return false; + } } else { const QStringRef &memberType = node->memberType; const QStringRef &name = node->name; @@ -662,7 +737,8 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) else property->customTypeNameIndex = emptyStringIndex; - property->nameIndex = registerString(name.toString()); + const QString propName = name.toString(); + property->nameIndex = registerString(propName); AST::SourceLocation loc = node->firstSourceLocation(); property->location.line = loc.startLine; @@ -710,25 +786,38 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) propertyValue += alias.at(2); } property->aliasPropertyValueIndex = registerString(propertyValue); - } else if (node->statement) - appendBinding(node->identifierToken, property->nameIndex, node->statement); + } else if (node->statement) { + qSwap(_propertyDeclaration, property); + appendBinding(node->identifierToken, _propertyDeclaration->nameIndex, node->statement); + qSwap(_propertyDeclaration, property); + } - _object->properties->append(property); + AST::SourceLocation errorLocation; + QString error; - if (node->isDefaultMember) { - if (_object->indexOfDefaultProperty != -1) { - QQmlError error; - error.setDescription(QCoreApplication::translate("QQmlParser","Duplicate default property")); - error.setLine(node->defaultToken.startLine); - error.setColumn(node->defaultToken.startColumn); - errors << error; - return false; - } - _object->indexOfDefaultProperty = _object->properties->count - 1; + if (illegalNames.contains(propName)) + error = tr("Illegal property name"); + else + error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation); + + if (!error.isEmpty()) { + if (errorLocation.startLine == 0) + errorLocation = node->identifierToken; + + QQmlError qmlError; + qmlError.setDescription(error); + qmlError.setLine(errorLocation.startLine); + qmlError.setColumn(errorLocation.startColumn); + errors << qmlError; + return false; } - // process QML-like initializers (e.g. property Object o: Object {}) - AST::Node::accept(node->binding, this); + if (node->binding) { + qSwap(_propertyDeclaration, property); + // process QML-like initializers (e.g. property Object o: Object {}) + AST::Node::accept(node->binding, this); + qSwap(_propertyDeclaration, property); + } } return false; @@ -739,8 +828,13 @@ bool QQmlCodeGenerator::visit(AST::UiSourceElement *node) if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) { _functions << funDecl; Function *f = New<Function>(); + f->functionDeclaration = funDecl; + AST::SourceLocation loc = funDecl->identifierToken; + f->location.line = loc.startLine; + f->location.column = loc.startColumn; f->index = _functions.size() - 1; - _object->functions->append(f); + f->nameIndex = registerString(funDecl->name.toString()); + _object->appendFunction(f); } else { QQmlError error; error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); @@ -798,7 +892,12 @@ QStringRef QQmlCodeGenerator::textRefAt(const AST::SourceLocation &first, const void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement) { + AST::SourceLocation loc = statement->firstSourceLocation(); + binding->valueLocation.line = loc.startLine; + binding->valueLocation.column = loc.startColumn; binding->type = QV4::CompiledData::Binding::Type_Invalid; + if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) { AST::ExpressionNode *expr = stmt->expression; @@ -844,23 +943,20 @@ void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement * qSwap(_object, object); } -void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex) +void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment) { QmlObject *object = 0; if (!resolveQualifiedId(&name, &object)) return; qSwap(_object, object); - appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex); + appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment); qSwap(_object, object); } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, AST::Statement *value) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex)) - return; - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { - setId(value); + setId(nameLocation, value); return; } @@ -870,14 +966,14 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i binding->location.column = nameLocation.startColumn; binding->flags = 0; setBindingValue(binding, value); - _object->bindings->append(binding); + QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); + if (!error.isEmpty()) { + recordError(nameLocation, error); + } } -void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem) +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { - if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem)) - return; - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { recordError(nameLocation, tr("Invalid component id specification")); return; @@ -887,26 +983,55 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i binding->propertyNameIndex = propertyNameIndex; binding->location.line = nameLocation.startLine; binding->location.column = nameLocation.startColumn; + + const QmlObject *obj = _objects.at(objectIndex); + binding->valueLocation = obj->location; + binding->flags = 0; - binding->type = QV4::CompiledData::Binding::Type_Object; + + if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) + binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + + // No type name on the initializer means it must be a group property + if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex) + binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + else + binding->type = QV4::CompiledData::Binding::Type_Object; + + if (isOnAssignment) + binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + if (isListItem) + binding->flags |= QV4::CompiledData::Binding::IsListItem; + binding->value.objectIndex = objectIndex; - _object->bindings->append(binding); + QString error = bindingsTarget()->appendBinding(binding, isListItem); + if (!error.isEmpty()) { + recordError(nameLocation, error); + } +} + +QmlObject *QQmlCodeGenerator::bindingsTarget() const +{ + if (_propertyDeclaration && _object->declarationsOverride) + return _object->declarationsOverride; + return _object; } -bool QQmlCodeGenerator::setId(AST::Statement *value) +bool QQmlCodeGenerator::setId(const AST::SourceLocation &idLocation, AST::Statement *value) { AST::SourceLocation loc = value->firstSourceLocation(); QStringRef str; AST::Node *node = value; if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node)) { - if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression)) + if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression)) { str = lit->value; - else + node = 0; + } else node = stmt->expression; } - if (str.isEmpty()) + if (node && str.isEmpty()) str = asStringRef(node); if (str.isEmpty()) @@ -926,12 +1051,14 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores")); } -#if 0 // ### - if (enginePrivate->v8engine()->illegalNames().contains(str)) - COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); -#endif + QString idQString(str.toString()); + if (illegalNames.contains(idQString)) + COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); - _object->idIndex = registerString(str.toString()); + if (_object->idIndex != emptyStringIndex) + COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); + + _object->idIndex = registerString(idQString); _object->locationOfIdProperty.line = loc.startLine; _object->locationOfIdProperty.column = loc.startColumn; @@ -940,61 +1067,57 @@ bool QQmlCodeGenerator::setId(AST::Statement *value) bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object) { - AST::UiQualifiedId *name = *nameToResolve; + AST::UiQualifiedId *qualifiedIdElement = *nameToResolve; + + if (qualifiedIdElement->name == QStringLiteral("id") && qualifiedIdElement->next) + COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property")); + + // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type. + QString currentName = qualifiedIdElement->name.toString(); + if (qualifiedIdElement->next) { + foreach (QV4::CompiledData::Import* import, _imports) + if (import->qualifierIndex != emptyStringIndex + && stringAt(import->qualifierIndex) == currentName) { + qualifiedIdElement = qualifiedIdElement->next; + currentName += QLatin1Char('.'); + currentName += qualifiedIdElement->name; + + if (!qualifiedIdElement->name.unicode()->isUpper()) + COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name")); - if (name->name == QStringLiteral("id") && name->next) - COMPILE_EXCEPTION(name->identifierToken, tr( "Invalid use of id property")); + break; + } + } *object = _object; - while (name->next) { + while (qualifiedIdElement->next) { Binding *binding = New<Binding>(); - binding->propertyNameIndex = registerString(name->name.toString()); - binding->location.line = name->identifierToken.startLine; - binding->location.column = name->identifierToken.startColumn; + binding->propertyNameIndex = registerString(currentName); + binding->location.line = qualifiedIdElement->identifierToken.startLine; + binding->location.column = qualifiedIdElement->identifierToken.startColumn; + binding->valueLocation.line = binding->valueLocation.column = 0; binding->flags = 0; - if (name->name.unicode()->isUpper()) + if (qualifiedIdElement->name.unicode()->isUpper()) binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; else binding->type = QV4::CompiledData::Binding::Type_GroupProperty; - int objIndex = defineQMLObject(0, 0); + int objIndex = defineQMLObject(0, AST::SourceLocation(), 0, 0); binding->value.objectIndex = objIndex; - (*object)->bindings->append(binding); + QString error = (*object)->appendBinding(binding, /*isListBinding*/false); + if (!error.isEmpty()) { + recordError(qualifiedIdElement->identifierToken, error); + return false; + } *object = _objects[objIndex]; - name = name->next; - } - *nameToResolve = name; - return true; -} - -bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem) -{ - const QString &name = jsGenerator->strings.at(nameIndex); - if (name.isEmpty()) - return true; - - // List items are implement by multiple bindings to the same name, so allow duplicates. - if (!isListItem) { - if (_propertyNames.contains(name)) - COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name")); - - _propertyNames.insert(name); + qualifiedIdElement = qualifiedIdElement->next; + if (qualifiedIdElement) + currentName = qualifiedIdElement->name.toString(); } - - if (name.at(0).isUpper()) - COMPILE_EXCEPTION(nameLocation, tr("Property names cannot begin with an upper case letter")); - -#if 0 // ### how to check against illegalNames when in separate thread? - if (enginePrivate->v8engine()->illegalNames().contains(prop.name.toString())) { - COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, - prop.nameLocation.column, - tr("Illegal property name")); - } -#endif - + *nameToResolve = qualifiedIdElement; return true; } @@ -1011,20 +1134,21 @@ void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const Q void QQmlCodeGenerator::collectTypeReferences() { foreach (QmlObject *obj, _objects) { - if (!stringAt(obj->inheritedTypeNameIndex).isEmpty()) - _typeReferences.add(obj->inheritedTypeNameIndex, obj->location); - - for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) { - if (prop->type >= QV4::CompiledData::Property::Custom) - _typeReferences.add(prop->customTypeNameIndex, prop->location); + if (obj->inheritedTypeNameIndex != emptyStringIndex) { + QV4::CompiledData::TypeReference &r = _typeReferences.add(obj->inheritedTypeNameIndex, obj->location); + r.needsCreation = true; } - for (Signal *sig = obj->qmlSignals->first; sig; sig = sig->next) - for (SignalParameter *param = sig->parameters->first; param; param = param->next) - if (!stringAt(param->customTypeNameIndex).isEmpty()) - _typeReferences.add(param->customTypeNameIndex, param->location); + for (const QmlProperty *prop = obj->firstProperty(); prop; prop = prop->next) { + if (prop->type >= QV4::CompiledData::Property::Custom) { + // ### FIXME: We could report the more accurate location here by using prop->location, but the old + // compiler can't and the tests expect it to be the object location right now. + QV4::CompiledData::TypeReference &r = _typeReferences.add(prop->customTypeNameIndex, obj->location); + r.needsCreation = true; + } + } - for (Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) _typeReferences.add(binding->propertyNameIndex, binding->location); } @@ -1082,10 +1206,10 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const int objectsSize = 0; foreach (QmlObject *o, output.objects) { objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count); + objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); int signalTableSize = 0; - for (Signal *s = o->qmlSignals->first; s; s = s->next) + for (const Signal *s = o->firstSignal(); s; s = s->next) signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count); objectsSize += signalTableSize; @@ -1128,46 +1252,45 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const quint32 nextOffset = sizeof(QV4::CompiledData::Object); - objectToWrite->nFunctions = o->functions->count; + objectToWrite->nFunctions = o->functionCount(); objectToWrite->offsetToFunctions = nextOffset; nextOffset += objectToWrite->nFunctions * sizeof(quint32); - objectToWrite->nProperties = o->properties->count; + objectToWrite->nProperties = o->propertyCount(); objectToWrite->offsetToProperties = nextOffset; nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property); - objectToWrite->nSignals = o->qmlSignals->count; + objectToWrite->nSignals = o->signalCount(); objectToWrite->offsetToSignals = nextOffset; nextOffset += objectToWrite->nSignals * sizeof(quint32); - objectToWrite->nBindings = o->bindings->count; + objectToWrite->nBindings = o->bindingCount(); objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions); - for (Function *f = o->functions->first; f; f = f->next) + for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = runtimeFunctionIndices[f->index]; char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; - for (QmlProperty *p = o->properties->first; p; p = p->next) { + for (const QmlProperty *p = o->firstProperty(); p; p = p->next) { QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr); *propertyToWrite = *p; propertiesPtr += sizeof(QV4::CompiledData::Property); } char *bindingPtr = objectPtr + objectToWrite->offsetToBindings; - for (Binding *b = o->bindings->first; b; b = b->next) { - QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); - *bindingToWrite = *b; - if (b->type == QV4::CompiledData::Binding::Type_Script) - bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex]; - bindingPtr += sizeof(QV4::CompiledData::Binding); - } + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingNoAlias); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isSignalHandler); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isAttachedProperty); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isGroupProperty); + bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingToAlias); + Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount())); quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals); quint32 signalTableSize = 0; char *signalPtr = objectPtr + nextOffset; - for (Signal *s = o->qmlSignals->first; s; s = s->next) { + for (const Signal *s = o->firstSignal(); s; s = s->next) { *signalOffsetTable++ = signalPtr - objectPtr; QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr); @@ -1184,7 +1307,7 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const signalPtr += size; } - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count); + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); objectPtr += signalTableSize; } @@ -1199,6 +1322,20 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const return qmlUnit; } +char *QmlUnitGenerator::writeBindings(char *bindingPtr, QmlObject *o, const QVector<int> &runtimeFunctionIndices, BindingFilter filter) const +{ + for (const Binding *b = o->firstBinding(); b; b = b->next) { + if (!(b->*(filter))()) + continue; + QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); + *bindingToWrite = *b; + if (b->type == QV4::CompiledData::Binding::Type_Script) + bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex]; + bindingPtr += sizeof(QV4::CompiledData::Binding); + } + return bindingPtr; +} + int QmlUnitGenerator::getStringId(const QString &str) const { return jsUnitGenerator->getStringId(str); @@ -1620,16 +1757,24 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, : enginePrivate(enginePrivate) , parsedQML(parsedQML) , unit(unit) + , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(enginePrivate))->illegalNames()) { } bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() { - foreach (QmlObject *obj, parsedQML->objects) { + for (int objectIndex = 0; objectIndex < parsedQML->objects.count(); ++objectIndex) { + QmlObject * const obj = parsedQML->objects.at(objectIndex); QString elementName = stringAt(obj->inheritedTypeNameIndex); if (elementName.isEmpty()) continue; - QQmlPropertyCache *cache = unit->resolvedTypes[obj->inheritedTypeNameIndex].createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + QQmlCompiledData::TypeReference *tr = unit->resolvedTypes.value(obj->inheritedTypeNameIndex); + QQmlCustomParser *customParser = (tr && tr->type) ? tr->type->customParser() : 0; + if (customParser && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) + continue; + QQmlPropertyCache *cache = unit->propertyCaches.value(objectIndex); + if (!cache) + continue; if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) return false; } @@ -1641,12 +1786,13 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // map from signal name defined in qml itself to list of parameters QHash<QString, QStringList> customSignals; - for (Binding *binding = obj->bindings->first; binding; binding = binding->next) { + for (Binding *binding = obj->firstBinding(); binding; binding = binding->next) { QString propertyName = stringAt(binding->propertyNameIndex); // Attached property? if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { QmlObject *attachedObj = parsedQML->objects[binding->value.objectIndex]; - QQmlType *type = unit->resolvedTypes.value(binding->propertyNameIndex).type; + QQmlCompiledData::TypeReference *typeRef = unit->resolvedTypes.value(binding->propertyNameIndex); + QQmlType *type = typeRef ? typeRef->type : 0; const QMetaObject *attachedType = type ? type->attachedPropertiesType() : 0; if (!attachedType) COMPILE_EXCEPTION(binding->location, tr("Non-existent attached object")); @@ -1659,10 +1805,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (!QQmlCodeGenerator::isSignalPropertyName(propertyName)) continue; - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment")); - } - PropertyResolver resolver(propertyCache); Q_ASSERT(propertyName.startsWith(QStringLiteral("on"))); @@ -1683,8 +1825,22 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); if (signal) { int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex); - foreach (const QByteArray ¶m, propertyCache->signalParameterNames(sigIndex)) - parameters << QString::fromUtf8(param); + sigIndex = propertyCache->originalClone(sigIndex); + + bool unnamedParameter = false; + + QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); + for (int i = 0; i < parameterNames.count(); ++i) { + const QString param = QString::fromUtf8(parameterNames.at(i)); + if (param.isEmpty()) + unnamedParameter = true; + else if (unnamedParameter) { + COMPILE_EXCEPTION(binding->location, tr("Signal uses unnamed parameter followed by named parameter.")); + } else if (illegalNames.contains(param)) { + COMPILE_EXCEPTION(binding->location, tr("Signal parameter \"%1\" hides global variable.").arg(param)); + } + parameters += param; + } } else { if (notInRevision) { // Try assinging it as a property later @@ -1693,7 +1849,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QString &originalPropertyName = stringAt(binding->propertyNameIndex); - const QQmlType *type = unit->resolvedTypes.value(obj->inheritedTypeNameIndex).type; + QQmlCompiledData::TypeReference *typeRef = unit->resolvedTypes.value(obj->inheritedTypeNameIndex); + const QQmlType *type = typeRef ? typeRef->type : 0; if (type) { COMPILE_EXCEPTION(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion())); } else { @@ -1705,12 +1862,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio // build cache if necessary if (customSignals.isEmpty()) { - for (Signal *signal = obj->qmlSignals->first; signal; signal = signal->next) { + for (const Signal *signal = obj->firstSignal(); signal; signal = signal->next) { const QString &signalName = stringAt(signal->nameIndex); customSignals.insert(signalName, signal->parameterStringList(parsedQML->jsGenerator.strings)); } - for (QmlProperty *property = obj->properties->first; property; property = property->next) { + for (const QmlProperty *property = obj->firstProperty(); property; property = property->next) { const QString propName = stringAt(property->nameIndex); customSignals.insert(propName, QStringList()); } @@ -1731,6 +1888,18 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio parameters = entry.value(); } + binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName); + + // Binding object to signal means connect the signal to the object's default method. + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; + continue; + } + + if (binding->type != QV4::CompiledData::Binding::Type_Script) { + COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment")); + } + QQmlJS::Engine &jsEngine = parsedQML->jsParserEngine; QQmlJS::MemoryPool *pool = jsEngine.pool(); @@ -1758,7 +1927,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio parsedQML->functions[binding->value.compiledScriptIndex] = functionDeclaration; binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; - binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName); } return true; } @@ -1773,11 +1941,11 @@ void SignalHandlerConverter::recordError(const QV4::CompiledData::Location &loca errors << error; } -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) { if (notInRevision) *notInRevision = false; - QQmlPropertyData *d = cache->property(name, 0, 0); + QQmlPropertyData *d = cache->property(name, object, context); // Find the first property while (d && d->isFunction()) @@ -1792,11 +1960,11 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev } -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) +QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) { if (notInRevision) *notInRevision = false; - QQmlPropertyData *d = cache->property(name, 0, 0); + QQmlPropertyData *d = cache->property(name, object, context); if (notInRevision) *notInRevision = false; while (d && !(d->isFunction())) @@ -1812,7 +1980,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis if (name.endsWith(QStringLiteral("Changed"))) { QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - d = property(propName, notInRevision); + d = property(propName, notInRevision, object, context); if (d) return cache->signal(d->notifyIndex); } |