aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-04-23 09:33:25 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-04-23 09:34:29 +0200
commit580fa7dc88aae23053e44ffa335a15f6af112a20 (patch)
tree5bc915d7c2e252739122d9441bd0d7fa7175d0c2 /src/qml
parent6767114285db9d0e16dc278d08f231e8561546b4 (diff)
parentc00283bb3bb966bf60c307ec8283bd98c12318bf (diff)
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp413
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h40
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h42
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp5
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp62
-rw-r--r--src/qml/compiler/qv4codegen.cpp465
-rw-r--r--src/qml/compiler/qv4codegen_p.h181
-rw-r--r--src/qml/compiler/qv4compileddata.cpp40
-rw-r--r--src/qml/compiler/qv4compileddata_p.h49
-rw-r--r--src/qml/compiler/qv4compiler.cpp57
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp7
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h7
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp54
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h7
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp45
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h22
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml134
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml61
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml54
-rw-r--r--src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml83
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc7
-rw-r--r--src/qml/doc/src/javascript/functionlist.qdoc61
-rw-r--r--src/qml/doc/src/javascript/hostenvironment.qdoc5
-rw-r--r--src/qml/doc/src/javascript/number.qdoc2
-rw-r--r--src/qml/doc/src/statemachine.qdoc4
-rw-r--r--src/qml/jit/jit.pri13
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp32
-rw-r--r--src/qml/jit/qv4baselinejit.cpp108
-rw-r--r--src/qml/jit/qv4baselinejit_p.h17
-rw-r--r--src/qml/jit/qv4blockscheduler.cpp208
-rw-r--r--src/qml/jit/qv4blockscheduler_p.h155
-rw-r--r--src/qml/jit/qv4domtree.cpp436
-rw-r--r--src/qml/jit/qv4domtree_p.h136
-rw-r--r--src/qml/jit/qv4graphbuilder.cpp72
-rw-r--r--src/qml/jit/qv4graphbuilder_p.h19
-rw-r--r--src/qml/jit/qv4ir.cpp4
-rw-r--r--src/qml/jit/qv4loopinfo.cpp199
-rw-r--r--src/qml/jit/qv4loopinfo_p.h159
-rw-r--r--src/qml/jit/qv4lowering.cpp200
-rw-r--r--src/qml/jit/qv4lowering_p.h107
-rw-r--r--src/qml/jit/qv4mi.cpp251
-rw-r--r--src/qml/jit/qv4mi_p.h627
-rw-r--r--src/qml/jit/qv4miblockset_p.h291
-rw-r--r--src/qml/jit/qv4node_p.h22
-rw-r--r--src/qml/jit/qv4operation.cpp18
-rw-r--r--src/qml/jit/qv4operation_p.h15
-rw-r--r--src/qml/jit/qv4schedulers.cpp912
-rw-r--r--src/qml/jit/qv4schedulers_p.h162
-rw-r--r--src/qml/jit/qv4tracingjit.cpp12
-rw-r--r--src/qml/jsapi/qjsvalue.cpp3
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp58
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4engine.cpp43
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--src/qml/jsruntime/qv4function.cpp1
-rw-r--r--src/qml/jsruntime/qv4function_p.h1
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4global_p.h3
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp7
-rw-r--r--src/qml/jsruntime/qv4include.cpp13
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp89
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h69
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp87
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h35
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp21
-rw-r--r--src/qml/jsruntime/qv4object.cpp115
-rw-r--r--src/qml/jsruntime/qv4object_p.h8
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h4
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp422
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h11
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp223
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h55
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp149
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h48
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h1
-rw-r--r--src/qml/jsruntime/qv4script.cpp1
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp3
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp20
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp40
-rw-r--r--src/qml/jsruntime/qv4value_p.h16
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp103
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h14
-rw-r--r--src/qml/memory/qv4mm.cpp78
-rw-r--r--src/qml/memory/qv4mm_p.h44
-rw-r--r--src/qml/parser/qqmljs.g18
-rw-r--r--src/qml/parser/qqmljsast.cpp19
-rw-r--r--src/qml/parser/qqmljsast_p.h28
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h4
-rw-r--r--src/qml/parser/qqmljsastvisitor.cpp6
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h40
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp4
-rw-r--r--src/qml/parser/qqmljsengine_p.h4
-rw-r--r--src/qml/parser/qqmljsglobal_p.h4
-rw-r--r--src/qml/parser/qqmljskeywords_p.h4
-rw-r--r--src/qml/parser/qqmljslexer_p.h4
-rw-r--r--src/qml/parser/qqmljsmemorypool_p.h4
-rw-r--r--src/qml/parser/qqmljssourcelocation_p.h4
-rw-r--r--src/qml/qml.pro1
-rw-r--r--src/qml/qml/qml.pri1
-rw-r--r--src/qml/qml/qqml.h5
-rw-r--r--src/qml/qml/qqmlbinding.cpp32
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp2
-rw-r--r--src/qml/qml/qqmlcomponent.cpp2
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp1
-rw-r--r--src/qml/qml/qqmlengine.cpp104
-rw-r--r--src/qml/qml/qqmlengine_p.h7
-rw-r--r--src/qml/qml/qqmlexpression.cpp1
-rw-r--r--src/qml/qml/qqmlglobal.cpp69
-rw-r--r--src/qml/qml/qqmlglobal_p.h4
-rw-r--r--src/qml/qml/qqmlimport.cpp13
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp82
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h21
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp1
-rw-r--r--src/qml/qml/qqmlmetatype.cpp3
-rw-r--r--src/qml/qml/qqmlmetatypedata.cpp31
-rw-r--r--src/qml/qml/qqmlmetatypedata_p.h6
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp1
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp99
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h24
-rw-r--r--src/qml/qml/qqmlpropertydata_p.h293
-rw-r--r--src/qml/qml/qqmlpropertyrawdata_p.h341
-rw-r--r--src/qml/qml/qqmltype.cpp43
-rw-r--r--src/qml/qml/qqmltype_p.h1
-rw-r--r--src/qml/qml/qqmltype_p_p.h14
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp62
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp29
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp153
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h3
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h1
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp6
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp4
-rw-r--r--src/qml/qml/v8/qv8engine_p.h15
-rw-r--r--src/qml/qtqmlglobal_p.h2
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp38
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h7
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h4
-rw-r--r--src/qml/types/qqmllistmodel.cpp18
-rw-r--r--src/qml/types/qqmllistmodel_p.h4
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h2
-rw-r--r--src/qml/types/qqmllistmodelworkeragent.cpp4
-rw-r--r--src/qml/types/qqmllistmodelworkeragent_p.h4
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp41
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h5
-rw-r--r--src/qml/types/qqmlobjectmodel.cpp4
-rw-r--r--src/qml/types/qqmlobjectmodel_p.h5
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h2
-rw-r--r--src/qml/types/qqmltablemodel.cpp731
-rw-r--r--src/qml/types/qqmltablemodel_p.h56
-rw-r--r--src/qml/types/qqmltablemodelcolumn.cpp200
-rw-r--r--src/qml/types/qqmltablemodelcolumn_p.h224
-rw-r--r--src/qml/types/qquickworkerscript.cpp12
-rw-r--r--src/qml/types/qquickworkerscript_p.h3
-rw-r--r--src/qml/types/types.pri6
-rw-r--r--src/qml/util/qqmladaptormodel.cpp7
158 files changed, 7543 insertions, 3338 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index ea5efcfc66..6e077ec44c 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -566,7 +566,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiQualifiedId *id)
void IRBuilder::accept(QQmlJS::AST::Node *node)
{
- QQmlJS::AST::Node::acceptChild(node, this);
+ QQmlJS::AST::Node::accept(node, this);
}
bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride)
@@ -974,7 +974,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
foe->node = funDecl;
foe->parentNode = funDecl;
foe->nameIndex = registerString(funDecl->name.toString());
- foe->disableAcceleratedLookups = false;
const int index = _object->functionsAndExpressions->append(foe);
Function *f = New<Function>();
@@ -1098,7 +1097,6 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
- expr->disableAcceleratedLookups = false;
const int index = bindingsTarget()->functionsAndExpressions->append(expr);
binding->value.compiledScriptIndex = index;
// We don't need to store the binding script as string, except for script strings
@@ -1113,6 +1111,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
translationData.commentIndex = 0; // empty string
+ translationData.padding = 0;
if (!args || !args->expression)
return; // no arguments, stop
@@ -1153,6 +1152,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
translationData.commentIndex = 0; // empty string, but unused
+ translationData.padding = 0;
if (!args || !args->expression)
return; // no arguments, stop
@@ -1825,19 +1825,13 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
- QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
+ QQmlJS::AST::UiProgram *qmlRoot,
const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames)
: QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
, sourceCode(sourceCode)
, jsEngine(jsEngine)
, qmlRoot(qmlRoot)
- , imports(imports)
, stringPool(stringPool)
- , _disableAcceleratedLookups(false)
- , _contextObject(nullptr)
- , _scopeObject(nullptr)
- , _qmlContextSlot(-1)
- , _importedScriptsSlot(-1)
{
m_globalNames = globalNames;
@@ -1845,18 +1839,6 @@ JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *
_fileNameIsUrl = true;
}
-void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject)
-{
- _idObjects = objectIds;
- _contextObject = contextObject;
- _scopeObject = nullptr;
-}
-
-void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
-{
- _scopeObject = scopeObject;
-}
-
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
auto qmlName = [&](const CompiledFunctionOrExpression &c) {
@@ -1921,7 +1903,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
body = body->finish();
}
- _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
@@ -1931,391 +1912,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
-{
- int qmlContextTemp = -1;
- int importedScriptsTemp = -1;
- qSwap(_qmlContextSlot, qmlContextTemp);
- qSwap(_importedScriptsSlot, importedScriptsTemp);
-
- int result = Codegen::defineFunction(name, ast, formals, body);
-
- qSwap(_importedScriptsSlot, importedScriptsTemp);
- qSwap(_qmlContextSlot, qmlContextTemp);
-
- return result;
-}
-
-#ifndef V4_BOOTSTRAP
-QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name)
-{
- QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr);
-
- if (pd && !cache->isAllowedInRevision(pd))
- return nullptr;
-
- return pd;
-}
-
-enum MetaObjectResolverFlags {
- AllPropertiesAreFinal = 0x1,
- LookupsIncludeEnums = 0x2,
- LookupsExcludeProperties = 0x4,
- ResolveTypeInformationOnly = 0x8
-};
-
-#if 0
-static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
-
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index);
-
-static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
- const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
-
- QQmlType type = resolver->qmlType;
-
- if (member->name->constData()->isUpper()) {
- bool ok = false;
- int value = type.enumValue(qmlEngine, *member->name, &ok);
- if (ok) {
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
- } else {
- int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok);
- if (ok) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initScopedEnumResolver(newResolver, type, index);
- return QV4::IR::DiscoveredType(newResolver);
- }
- }
- }
-
- if (type.isCompositeSingleton()) {
- QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url);
- Q_ASSERT(tdata);
- tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType()
- // When a singleton tries to reference itself, it may not be complete yet.
- if (tdata->isComplete()) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId));
- newResolver->flags |= AllPropertiesAreFinal;
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
- } else if (type.isSingleton()) {
- const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject;
- if (singletonMeta) { // QJSValue-based singletons cannot be accelerated
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->cache(singletonMeta));
- member->kind = QV4::IR::Member::MemberOfSingletonObject;
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
- }
-#if 0
- else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
- // Right now the attached property IDs are not stable and cannot be embedded in the
- // code that is cached on disk.
- QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine));
- return newResolver->resolveMember(qmlEngine, newResolver, member);
- }
-#endif
-
- return result;
-}
-
-static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveQmlType;
- resolver->qmlType = qmlType;
- resolver->typenameCache = 0;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveImportNamespace(
- QQmlEnginePrivate *, const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
- QQmlTypeNameCache *typeNamespace = resolver->typenameCache;
- const QQmlImportRef *importNamespace = resolver->import;
-
- QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace);
- if (r.isValid()) {
- member->freeOfSideEffects = true;
- if (r.scriptIndex != -1) {
- // TODO: remember the index and replace with subscript later.
- result = QV4::IR::VarType;
- } else if (r.type.isValid()) {
- // TODO: Propagate singleton information, so that it is loaded
- // through the singleton getter in the run-time. Until then we
- // can't accelerate access :(
- if (!r.type.isSingleton()) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initQmlTypeResolver(newResolver, r.type);
- return QV4::IR::DiscoveredType(newResolver);
- }
- } else {
- Q_ASSERT(false); // How can this happen?
- }
- }
-
- return result;
-}
-
-static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver,
- QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace)
-{
- resolver->resolveMember = &resolveImportNamespace;
- resolver->import = importNamespace;
- resolver->typenameCache = imports;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveMetaObjectProperty(
- QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- QV4::IR::Type result = QV4::IR::VarType;
- QQmlPropertyCache *metaObject = resolver->propertyCache;
-
- if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) {
- const QMetaObject *mo = metaObject->createMetaObject();
- QByteArray enumName = member->name->toUtf8();
- for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) {
- QMetaEnum metaEnum = mo->enumerator(ii);
- bool ok;
- int value = metaEnum.keyToValue(enumName.constData(), &ok);
- if (ok) {
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
- }
- }
- }
-
- if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject &&
- qmlEngine && !(resolver->flags & LookupsExcludeProperties)) {
- QQmlPropertyData *property = member->property;
- if (!property && metaObject) {
- if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) {
- const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal))
- && !candidate->isFunction();
-
- if (lookupHints()
- && !(resolver->flags & AllPropertiesAreFinal)
- && !candidate->isFinal()
- && !candidate->isFunction()
- && candidate->isDirect()) {
- qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL";
- }
-
- if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) {
- property = candidate;
- member->inhibitTypeConversionOnWrite = true;
- if (!(resolver->flags & ResolveTypeInformationOnly))
- member->property = candidate; // Cache for next iteration and isel needs it.
- }
- }
- }
-
- if (property) {
- // Enums cannot be mapped to IR types, they need to go through the run-time handling
- // of accepting strings that will then be converted to the right values.
- if (property->isEnum())
- return QV4::IR::VarType;
-
- switch (property->propType()) {
- case QMetaType::Bool: result = QV4::IR::BoolType; break;
- case QMetaType::Int: result = QV4::IR::SInt32Type; break;
- case QMetaType::Double: result = QV4::IR::DoubleType; break;
- case QMetaType::QString: result = QV4::IR::StringType; break;
- default:
- if (property->isQObject()) {
- if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- return QV4::IR::DiscoveredType(newResolver);
- }
- } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) {
- if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) {
- auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
- newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, cache);
- newResolver->flags |= ResolveTypeInformationOnly;
- return QV4::IR::DiscoveredType(newResolver);
- }
- }
- break;
- }
- }
- }
-
- return result;
-}
-
-static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveMetaObjectProperty;
- resolver->propertyCache = metaObject;
- resolver->flags = 0;
-}
-
-static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
- const QV4::IR::MemberExpressionResolver *resolver,
- QV4::IR::Member *member)
-{
- if (!member->name->constData()->isUpper())
- return QV4::IR::VarType;
-
- QQmlType type = resolver->qmlType;
- int index = resolver->flags;
-
- bool ok = false;
- int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok);
- if (!ok)
- return QV4::IR::VarType;
- member->setEnumValue(value);
- return QV4::IR::SInt32Type;
-}
-
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index)
-{
- Q_ASSERT(resolver);
-
- resolver->resolveMember = &resolveScopedEnum;
- resolver->qmlType = qmlType;
- resolver->flags = index;
-}
-#endif
-
-#endif // V4_BOOTSTRAP
-
-void JSCodeGen::beginFunctionBodyHook()
-{
- _qmlContextSlot = bytecodeGenerator->newRegister();
- _importedScriptsSlot = bytecodeGenerator->newRegister();
-
-#ifndef V4_BOOTSTRAP
- Instruction::LoadQmlContext load;
- load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
- bytecodeGenerator->addInstruction(load);
-
-#if 0
- temp->type = QV4::IR::QObjectType;
- temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- initMetaObjectResolver(temp->memberResolver, _scopeObject);
- auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0);
- name->type = temp->type;
-#endif
-
- Instruction::LoadQmlImportedScripts loadScripts;
- loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot();
- bytecodeGenerator->addInstruction(loadScripts);
-#endif
-}
-
-QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
-{
-#ifndef V4_BOOTSTRAP
- if (_disableAcceleratedLookups)
- return Reference();
-
- // Implement QML lookup semantics in the current file context.
- //
- // Note: We do not check if properties of the qml scope object or context object
- // are final. That's because QML tries to get as close as possible to lexical scoping,
- // which means in terms of properties that only those visible at compile time are chosen.
- // I.e. access to a "foo" property declared within the same QML component as "property int foo"
- // will always access that instance and as integer. If a sub-type implements its own property string foo,
- // then that one is not chosen for accesses from within this file, because it wasn't visible at compile
- // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated
- // with the correct QML context.
-
- // Look for IDs first.
- for (const IdMapping &mapping : qAsConst(_idObjects)) {
- if (name == mapping.name) {
- if (_context->contextType == QV4::Compiler::ContextType::Binding)
- _context->idObjectDependencies.insert(mapping.idIndex);
-
- Instruction::LoadIdObject load;
- load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
- load.index = mapping.idIndex;
-
- Reference result = Reference::fromAccumulator(this);
- bytecodeGenerator->addInstruction(load);
- result.isReadonly = true;
- return result;
- }
- }
-
- if (name.at(0).isUpper()) {
- QQmlTypeNameCache::Result r = imports->query(name);
- if (r.isValid()) {
- if (r.scriptIndex != -1) {
- Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot);
- return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex)));
- } else if (r.type.isValid()) {
- return Reference::fromName(this, name);
- } else {
- Q_ASSERT(r.importNamespace);
- return Reference::fromName(this, name);
- }
- }
- }
-
- if (_scopeObject) {
- QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
- if (data) {
- // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (data->isFunction())
- return Reference::fromName(this, name);
-
- Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
- Reference::PropertyCapturePolicy capturePolicy;
- if (!data->isConstant() && !data->isQmlBinding())
- capturePolicy = Reference::CaptureAtRuntime;
- else
- capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
- return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
- }
- }
-
- if (_contextObject) {
- QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name);
- if (data) {
- // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (data->isFunction())
- return Reference::fromName(this, name);
-
- Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
- Reference::PropertyCapturePolicy capturePolicy;
- if (!data->isConstant() && !data->isQmlBinding())
- capturePolicy = Reference::CaptureAtRuntime;
- else
- capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
- return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
- }
- }
-#else
- Q_UNUSED(name)
-#endif // V4_BOOTSTRAP
- return Reference();
-}
-
#ifndef V4_BOOTSTRAP
QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
@@ -2435,7 +2031,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
b->value.compiledScriptIndex = functionIndices.count() - 1;
QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
- foe->disableAcceleratedLookups = true;
foe->nameIndex = 0;
QQmlJS::AST::ExpressionNode *expr;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 8512b22fbd..298fe7dd92 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -278,7 +278,6 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
- bool disableAcceleratedLookups = false;
CompiledFunctionOrExpression *next = nullptr;
};
@@ -431,6 +430,12 @@ public:
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
+ void throwRecursionDepthError() override
+ {
+ recordError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
+
void accept(QQmlJS::AST::Node *node);
// returns index in _objects
@@ -533,47 +538,16 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot,
- QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
-
- struct IdMapping
- {
- QString name;
- int idIndex;
- QQmlPropertyCache *type;
- };
- typedef QVector<IdMapping> ObjectIdMapping;
-
- void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject);
- void beginObjectScope(QQmlPropertyCache *scopeObject);
+ const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
- int defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::StatementList *body) override;
-
-protected:
- void beginFunctionBodyHook() override;
- bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; }
- Reference fallbackNameLookup(const QString &name) override;
-
private:
- // returns nullptr if lookup needs to happen by name
- QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name);
-
QString sourceCode;
QQmlJS::Engine *jsEngine; // needed for memory pool
QQmlJS::AST::UiProgram *qmlRoot;
- QQmlTypeNameCache *imports;
const QV4::Compiler::StringTableGenerator *stringPool;
-
- bool _disableAcceleratedLookups;
- ObjectIdMapping _idObjects;
- QQmlPropertyCache *_contextObject;
- QQmlPropertyCache *_scopeObject;
- int _qmlContextSlot;
- int _importedScriptsSlot;
};
struct Q_QML_PRIVATE_EXPORT IRLoader {
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 7d416561bb..21d653af55 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -578,7 +578,7 @@ public:
private:
void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
- QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags);
+ QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -693,11 +693,6 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
QQmlPropertyData::Flags *propertyFlags)
{
- const int targetObjectIndex = objectForId(component, alias.targetObjectId);
- Q_ASSERT(targetObjectIndex >= 0);
-
- const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
-
*type = 0;
bool writable = false;
bool resettable = false;
@@ -705,11 +700,36 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property
propertyFlags->isAlias = true;
if (alias.aliasToLocalAlias) {
- auto targetAlias = targetObject.aliasesBegin();
- for (uint i = 0; i < alias.localAliasIndex; ++i)
- ++targetAlias;
- return propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags);
- } else if (alias.encodedMetaPropertyIndex == -1) {
+ const QV4::CompiledData::Alias *lastAlias = &alias;
+ QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
+
+ do {
+ const CompiledObject *targetObject = objectContainer->objectAt(
+ objectForId(component, lastAlias->targetObjectId));
+ Q_ASSERT(targetObject);
+
+ auto nextAlias = targetObject->aliasesBegin();
+ for (uint i = 0; i < lastAlias->localAliasIndex; ++i)
+ ++nextAlias;
+
+ const QV4::CompiledData::Alias *targetAlias = &(*nextAlias);
+ if (seenAliases.contains(targetAlias)) {
+ return QQmlCompileError(targetAlias->location,
+ QQmlPropertyCacheCreatorBase::tr("Cyclic alias"));
+ }
+
+ seenAliases.append(targetAlias);
+ lastAlias = targetAlias;
+ } while (lastAlias->aliasToLocalAlias);
+
+ return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags);
+ }
+
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ if (alias.encodedMetaPropertyIndex == -1) {
Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
if (!typeRef) {
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index 4714f505a7..8c06760d42 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -693,7 +693,10 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
+ auto typeName = QMetaType::typeName(property->propType());
+ return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object")
+ .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>"))
+ .arg(propertyName));
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 70b048d737..239f04a58f 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -146,8 +145,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
document->jsModule.fileName = typeData->urlString();
document->jsModule.finalUrl = typeData->finalUrlString();
QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
- document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
- v4CodeGenerator.setUseFastLookups(false);
+ document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
@@ -767,10 +765,6 @@ void QQmlScriptStringScanner::scan()
if (!pd || pd->propType() != scriptStringMetaType)
continue;
- QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
- if (foe)
- foe->disableAcceleratedLookups = true;
-
QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
binding->stringIndex = compiler->registerString(script);
}
@@ -786,6 +780,23 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t
{
}
+static bool isUsableComponent(const QMetaObject *metaObject)
+{
+ // The metaObject is a component we're interested in if it either is a QQmlComponent itself
+ // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include
+ // qqmldelegatecomponent_p.h because it belongs to QtQmlModels.
+
+ if (metaObject == &QQmlComponent::staticMetaObject)
+ return true;
+
+ for (; metaObject; metaObject = metaObject->superClass()) {
+ if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0)
+ return true;
+ }
+
+ return false;
+}
+
void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache)
{
QmlIR::PropertyResolver propertyResolver(propertyCache);
@@ -807,15 +818,9 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
firstMetaObject = tr->type.metaObject();
else if (tr->compilationUnit)
firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
- // 1: test for QQmlComponent
- if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ if (isUsableComponent(firstMetaObject))
continue;
- // 2: test for QQmlAbstractDelegateComponent
- while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject)
- firstMetaObject = firstMetaObject->superClass();
- if (firstMetaObject)
- continue;
- // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping
+ // if here, not a QQmlComponent, so needs wrapping
QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
@@ -1324,24 +1329,6 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject)
contextObject = componentBinding->value.objectIndex;
}
- QmlIR::JSCodeGen::ObjectIdMapping idMapping;
- idMapping.reserve(obj->namedObjectsInComponent.size());
- for (int i = 0; i < obj->namedObjectsInComponent.size(); ++i) {
- const int objectIndex = obj->namedObjectsInComponent.at(i);
- QmlIR::JSCodeGen::IdMapping m;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- m.name = stringAt(obj->idNameIndex);
- m.idIndex = obj->id;
- m.type = propertyCaches->at(objectIndex);
-
- auto *tref = resolvedType(obj->inheritedTypeNameIndex);
- if (tref && tref->isFullyDynamicType)
- m.type = nullptr;
-
- idMapping << m;
- }
- v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject));
-
if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
return false;
@@ -1355,16 +1342,9 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
return true;
if (object->functionsAndExpressions->count > 0) {
- QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex);
- v4CodeGen->beginObjectScope(scopeObject);
-
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
- for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) {
- const bool haveCustomParser = customParsers.contains(object->inheritedTypeNameIndex);
- if (haveCustomParser)
- foe->disableAcceleratedLookups = true;
+ for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
functionsToCompile << *foe;
- }
const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile);
const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors();
if (!jsErrors.isEmpty()) {
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 448fbff27b..88d3dbe9c5 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -100,9 +100,10 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
, hasError(false)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
+ pushExpr();
}
-const char *globalNames[] = {
+const char *Codegen::s_globalNames[] = {
"isNaN",
"parseFloat",
"String",
@@ -182,7 +183,7 @@ void Codegen::generateFromProgram(const QString &fileName,
//
// Since this can be called from the loader thread we can't get the list
// directly from the engine, so let's hardcode the most important ones here
- for (const char **g = globalNames; *g != nullptr; ++g)
+ for (const char **g = s_globalNames; *g != nullptr; ++g)
m_globalNames << QString::fromLatin1(*g);
}
@@ -264,7 +265,7 @@ Context *Codegen::enterBlock(Node *node)
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
- return _expr.result();
+ return exprResult();
if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
@@ -293,8 +294,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
}
case UPlus: {
expr.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
return Reference::fromAccumulator(this);
}
case Not: {
@@ -310,11 +311,11 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
return Reference::fromAccumulator(this);
}
case PostIncrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
@@ -330,17 +331,17 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
}
case PostDecrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
- Instruction::UPlus uplus;
- bytecodeGenerator->addInstruction(uplus);
+ Instruction::UPlus uplus = {};
+ bytecodeGenerator->addTracingInstruction(uplus);
Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
@@ -356,7 +357,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
@@ -368,22 +369,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
void Codegen::addCJump()
{
- bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
- _expr.iftrue(), _expr.iffalse());
-}
-
-void Codegen::accept(Node *node)
-{
- if (hasError)
- return;
-
- if (node)
- node->accept(this);
+ const Result &expression = currentExpr();
+ bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(),
+ expression.iftrue(), expression.iffalse());
}
void Codegen::statement(Statement *ast)
{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
bytecodeGenerator->setLocation(ast->firstSourceLocation());
@@ -399,23 +391,21 @@ void Codegen::statement(ExpressionNode *ast)
if (! ast) {
return;
} else {
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
- Result r(nx);
- qSwap(_expr, r);
+ pushExpr(Result(nx));
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
qSwap(_volatileMemoryLocations, vLocs);
- qSwap(_expr, r);
+ Reference result = popResult();
if (hasError)
return;
- if (r.result().loadTriggersSideEffect())
- r.result().loadInAccumulator(); // triggers side effects
+ if (result.loadTriggersSideEffect())
+ result.loadInAccumulator(); // triggers side effects
}
}
@@ -428,11 +418,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
if (!ast)
return;
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r(iftrue, iffalse, trueBlockFollowsCondition);
- qSwap(_expr, r);
+ pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition));
accept(ast);
- qSwap(_expr, r);
+ Result r = popExpr();
if (hasError)
return;
@@ -450,18 +438,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
}
}
-Codegen::Reference Codegen::expression(ExpressionNode *ast)
-{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r;
- if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
- }
- return r.result();
-}
-
void Codegen::program(Program *ast)
{
if (ast) {
@@ -875,17 +851,13 @@ bool Codegen::visit(ExportDeclaration *ast)
Reference exportedValue;
if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<FunctionExpression*>(fdecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<ClassExpression*>(classDecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
exportedValue = expression(expr);
}
@@ -1068,7 +1040,7 @@ bool Codegen::visit(ClassExpression *ast)
(void) ctor.storeRetainAccumulator();
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1151,7 +1123,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
if (!it) {
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
@@ -1246,7 +1218,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
array.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1262,7 +1234,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(index));
+ setExprResult(Reference::fromSuperProperty(index));
return false;
}
base = base.storeOnStack();
@@ -1272,17 +1244,17 @@ bool Codegen::visit(ArrayMemberExpression *ast)
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
if (arrayIndex == UINT_MAX) {
- _expr.setResult(Reference::fromMember(base, str->value.toString()));
+ setExprResult(Reference::fromMember(base, str->value.toString()));
return false;
}
Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
Reference index = expression(ast->expression);
if (hasError)
return false;
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
@@ -1313,12 +1285,13 @@ bool Codegen::visit(BinaryExpression *ast)
TailCallBlocker blockTailCalls(this);
if (ast->op == QSOperator::And) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iftrue = bytecodeGenerator->newLabel();
- condition(ast->left, &iftrue, _expr.iffalse(), true);
+ condition(ast->left, &iftrue, currentExpr().iffalse(), true);
iftrue.link();
blockTailCalls.unblock();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iftrue = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1340,15 +1313,16 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iffalse = bytecodeGenerator->newLabel();
- condition(ast->left, _expr.iftrue(), &iffalse, false);
+ condition(ast->left, currentExpr().iftrue(), &iffalse, false);
iffalse.link();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iffalse = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1370,7 +1344,7 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Assign) {
@@ -1381,9 +1355,9 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
right = right.storeOnStack();
destructurePattern(p, right);
- if (!_expr.accept(nx)) {
+ if (!exprAccept(nx)) {
right.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
}
@@ -1403,10 +1377,10 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
r.loadInAccumulator();
- if (_expr.accept(nx))
- _expr.setResult(left.storeConsumeAccumulator());
+ if (exprAccept(nx))
+ setExprResult(left.storeConsumeAccumulator());
else
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
return false;
}
@@ -1449,7 +1423,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
break;
}
@@ -1461,7 +1435,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
// intentional fall-through!
@@ -1487,7 +1461,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right;
if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
visit(rhs);
- right = _expr.result();
+ right = exprResult();
} else {
left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
right = expression(ast->right);
@@ -1495,7 +1469,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@@ -1672,7 +1646,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictEqual cmp;
@@ -1683,7 +1657,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictNotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictNotEqual cmp;
@@ -1694,7 +1668,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Equal: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpEq cmp;
@@ -1705,7 +1679,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::NotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpNe cmp;
@@ -1716,7 +1690,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Gt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGt cmp;
@@ -1727,7 +1701,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Ge: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGe cmp;
@@ -1738,7 +1712,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Lt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLt cmp;
@@ -1749,7 +1723,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Le:
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLe cmp;
@@ -1765,59 +1739,46 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
return Reference::fromAccumulator(this);
}
-static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
-{
- switch (oper) {
- case QSOperator::StrictEqual: return QSOperator::StrictEqual;
- case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual;
- case QSOperator::Equal: return QSOperator::Equal;
- case QSOperator::NotEqual: return QSOperator::NotEqual;
- case QSOperator::Gt: return QSOperator::Le;
- case QSOperator::Ge: return QSOperator::Lt;
- case QSOperator::Lt: return QSOperator::Ge;
- case QSOperator::Le: return QSOperator::Gt;
- default: Q_UNIMPLEMENTED(); return QSOperator::Invalid;
- }
-}
-
Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
{
- if (left.isConstant()) {
- oper = operatorForSwappedOperands(oper);
- qSwap(left, right);
- }
+ // See if we can generate specialized comparison instructions:
+ if (oper == QSOperator::Equal || oper == QSOperator::NotEqual) {
+ // Because == and != are reflexive, we can do the following:
+ if (left.isConstant() && !right.isConstant())
+ qSwap(left, right); // null==a -> a==null
- if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
- Value c = Value::fromReturnedValue(right.constant);
- if (c.isNull() || c.isUndefined()) {
- left.loadInAccumulator();
- if (oper == QSOperator::Equal) {
- Instruction::CmpEqNull cmp;
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- } else if (oper == QSOperator::NotEqual) {
- Instruction::CmpNeNull cmp;
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- }
- } else if (c.isInt32()) {
- left.loadInAccumulator();
- if (oper == QSOperator::Equal) {
- Instruction::CmpEqInt cmp;
- cmp.lhs = c.int_32();
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- } else if (oper == QSOperator::NotEqual) {
- Instruction::CmpNeInt cmp;
- cmp.lhs = c.int_32();
- bytecodeGenerator->addInstruction(cmp);
- addCJump();
- return Reference();
- }
+ if (right.isConstant()) {
+ Value c = Value::fromReturnedValue(right.constant);
+ if (c.isNull() || c.isUndefined()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ } else if (c.isInt32()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ }
}
}
@@ -1902,8 +1863,6 @@ bool Codegen::visit(CallExpression *ast)
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
- case Reference::QmlScopeObject:
- case Reference::QmlContextObject:
base = base.asLValue();
break;
case Reference::Name:
@@ -1953,7 +1912,7 @@ bool Codegen::visit(CallExpression *ast)
bytecodeGenerator->addInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1965,21 +1924,7 @@ bool Codegen::visit(CallExpression *ast)
void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject)
{
//### Do we really need all these call instructions? can's we load the callee in a temp?
- if (base.type == Reference::QmlScopeObject) {
- Instruction::CallScopeObjectProperty call;
- call.base = base.qmlBase.stackSlot();
- call.name = base.qmlCoreIndex;
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
- } else if (base.type == Reference::QmlContextObject) {
- Instruction::CallContextObjectProperty call;
- call.base = base.qmlBase.stackSlot();
- call.name = base.qmlCoreIndex;
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
- } else if (base.type == Reference::Member) {
+ if (base.type == Reference::Member) {
if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
@@ -2009,11 +1954,19 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else if (!disable_lookups && useFastLookups && base.global) {
- Instruction::CallGlobalLookup call;
- call.index = registerGlobalGetterLookup(base.nameAsIndex());
- call.argc = calldata.argc;
- call.argv = calldata.argv;
- bytecodeGenerator->addTracingInstruction(call);
+ if (base.qmlGlobal) {
+ Instruction::CallQmlContextPropertyLookup call;
+ call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Instruction::CallGlobalLookup call;
+ call.index = registerGlobalGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ }
} else {
Instruction::CallName call;
call.name = base.nameAsIndex();
@@ -2046,7 +1999,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
bytecodeGenerator->addTracingInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
@@ -2142,7 +2095,7 @@ bool Codegen::visit(ConditionalExpression *ast)
ko.loadInAccumulator();
jump_endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2172,7 +2125,7 @@ bool Codegen::visit(DeleteExpression *ast)
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
case Reference::Name: {
if (_context->isStrict) {
@@ -2182,7 +2135,7 @@ bool Codegen::visit(DeleteExpression *ast)
Instruction::DeleteName del;
del.name = expr.nameAsIndex();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Member: {
@@ -2197,7 +2150,7 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.propertyBase.stackSlot();
del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Subscript: {
@@ -2207,14 +2160,14 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
default:
break;
}
// [[11.4.1]] Return true if it's not a reference
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2223,7 +2176,7 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
@@ -2232,7 +2185,7 @@ bool Codegen::visit(SuperLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromSuper(this));
+ setExprResult(Reference::fromSuper(this));
return false;
}
@@ -2250,12 +2203,12 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
Reference r = referenceForName(QStringLiteral("new.target"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
}
@@ -2268,10 +2221,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
load.stringId = registerString(ast->name.toString());
bytecodeGenerator->addInstruction(load);
Reference property = Reference::fromAccumulator(this).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(property));
+ setExprResult(Reference::fromSuperProperty(property));
return false;
}
- _expr.setResult(Reference::fromMember(base, ast->name.toString()));
+ setExprResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
@@ -2281,12 +2234,15 @@ bool Codegen::visit(TaggedTemplate *ast)
return false;
RegisterScope scope(this);
+ return handleTaggedTemplate(expression(ast->base), ast);
+}
- int functionObject = -1, thisObject = -1;
-
- Reference base = expression(ast->base);
+bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
+{
if (hasError)
return false;
+
+ int functionObject = -1, thisObject = -1;
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
@@ -2347,7 +2303,7 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2379,14 +2335,10 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co
return r;
}
- // This hook allows implementing QML lookup semantics
- Reference fallback = fallbackNameLookup(name);
- if (fallback.type != Reference::Invalid)
- return fallback;
-
Reference r = Reference::fromName(this, name);
- r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global);
- if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name))
+ r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal);
+ r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal;
+ if (!r.global && !r.qmlGlobal && m_globalNames.contains(name))
r.global = true;
return r;
}
@@ -2402,18 +2354,12 @@ void Codegen::loadClosure(int closureId)
}
}
-Codegen::Reference Codegen::fallbackNameLookup(const QString &name)
-{
- Q_UNUSED(name)
- return Reference();
-}
-
bool Codegen::visit(IdentifierExpression *ast)
{
if (hasError)
return false;
- _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
+ setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -2463,7 +2409,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
// set the result up as the thisObject
Reference::fromAccumulator(this).storeOnStack(CallData::This);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
bool Codegen::visit(NewExpression *ast)
@@ -2512,7 +2458,7 @@ bool Codegen::visit(NotExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Not, expression(ast->expression)));
+ setExprResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -2521,10 +2467,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
- if (_expr.accept(cx))
- bytecodeGenerator->jump().link(*_expr.iffalse());
+ if (exprAccept(cx))
+ bytecodeGenerator->jump().link(*currentExpr().iffalse());
else
- _expr.setResult(Reference::fromConst(this, Encode::null()));
+ setExprResult(Reference::fromConst(this, Encode::null()));
return false;
}
@@ -2534,7 +2480,7 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
+ setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
@@ -2646,8 +2592,7 @@ bool Codegen::visit(ObjectPattern *ast)
call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
- Reference result = Reference::fromAccumulator(this);
- _expr.setResult(result);
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2666,7 +2611,7 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PostDecrement, expr));
+ setExprResult(unop(PostDecrement, expr));
return false;
}
@@ -2686,7 +2631,7 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PostIncrement, expr));
+ setExprResult(unop(PostIncrement, expr));
return false;
}
@@ -2704,7 +2649,7 @@ bool Codegen::visit(PreDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PreDecrement, expr));
+ setExprResult(unop(PreDecrement, expr));
return false;
}
@@ -2723,7 +2668,7 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PreIncrement, expr));
+ setExprResult(unop(PreIncrement, expr));
return false;
}
@@ -2734,7 +2679,7 @@ bool Codegen::visit(RegExpLiteral *ast)
auto r = Reference::fromStackSlot(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::MoveRegExp instr;
instr.regExpId = jsUnitGenerator->registerRegExp(ast);
@@ -2750,7 +2695,7 @@ bool Codegen::visit(StringLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::LoadRuntimeString instr;
instr.stringId = registerString(ast->value.toString());
@@ -2800,7 +2745,7 @@ bool Codegen::visit(TemplateLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
@@ -2813,10 +2758,10 @@ bool Codegen::visit(ThisExpression *)
if (_context->isArrowFunction) {
Reference r = referenceForName(QStringLiteral("this"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
- _expr.setResult(Reference::fromThis(this));
+ setExprResult(Reference::fromThis(this));
return false;
}
@@ -2826,7 +2771,7 @@ bool Codegen::visit(TildeExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Compl, expression(ast->expression)));
+ setExprResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -2835,7 +2780,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2861,7 +2806,7 @@ bool Codegen::visit(TypeOfExpression *ast)
Instruction::TypeofValue instr;
bytecodeGenerator->addInstruction(instr);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2872,7 +2817,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UMinus, expression(ast->expression)));
+ setExprResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2882,7 +2827,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UPlus, expression(ast->expression)));
+ setExprResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2895,7 +2840,7 @@ bool Codegen::visit(VoidExpression *ast)
TailCallBlocker blockTailCalls(this);
statement(ast->expression);
- _expr.setResult(Reference::fromConst(this, Encode::undefined()));
+ setExprResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@@ -2909,7 +2854,7 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (_functionContext->contextType == ContextType::Binding)
referenceForName(ast->name.toString(), true).loadInAccumulator();
- _expr.accept(nx);
+ exprAccept(nx);
return false;
}
@@ -2965,7 +2910,7 @@ bool Codegen::visit(YieldExpression *ast)
done.link();
lhsValue.loadInAccumulator();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -2976,7 +2921,7 @@ bool Codegen::visit(YieldExpression *ast)
BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
emitReturn(acc);
jump.link();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -3115,8 +3060,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bytecodeGenerator->addInstruction(yield);
}
- beginFunctionBodyHook();
-
statementList(body);
if (!hasError) {
@@ -3855,8 +3798,14 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
{
VolatileMemoryLocations locs;
+ Codegen *parent;
public:
+ VolatileMemoryLocationScanner(Codegen *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()),
+ parent(parent)
+ {}
+
Codegen::VolatileMemoryLocations scan(AST::Node *s)
{
s->accept(this);
@@ -3921,25 +3870,41 @@ public:
}
}
+ void throwRecursionDepthError() override
+ {
+ parent->throwRecursionDepthError();
+ }
+
private:
- void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
+ void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) {
class Collector: public QQmlJS::AST::Visitor {
+ private:
QVector<QStringView> &ids;
+ VolatileMemoryLocationScanner *parent;
+
public:
- Collector(QVector<QStringView> &ids): ids(ids) {}
- virtual bool visit(IdentifierExpression *ie) {
+ Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent)
+ {}
+
+ bool visit(IdentifierExpression *ie) final {
ids.append(ie->name);
return false;
}
+
+ void throwRecursionDepthError() final
+ {
+ parent->throwRecursionDepthError();
+ }
};
- Collector collector(ids);
+ Collector collector(ids, this);
node->accept(&collector);
}
};
-Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
+Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast)
{
- VolatileMemoryLocationScanner scanner;
+ VolatileMemoryLocationScanner scanner(this);
return scanner.scan(ast);
}
@@ -4041,10 +4006,6 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
return index == other.index;
case Const:
return constant == other.constant;
- case QmlScopeObject:
- case QmlContextObject:
- return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex
- && capturePolicy == other.capturePolicy;
}
return true;
}
@@ -4102,9 +4063,7 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
Codegen::Reference Codegen::Reference::baseObject() const
{
- if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
- return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
- } else if (type == Reference::Member) {
+ if (type == Reference::Member) {
RValue rval = propertyBase;
if (!rval.isValid())
return Reference::fromConst(codegen, Encode::undefined());
@@ -4189,8 +4148,6 @@ bool Codegen::Reference::storeWipesAccumulator() const
case Name:
case Member:
case Subscript:
- case QmlScopeObject:
- case QmlContextObject:
return true;
}
}
@@ -4270,18 +4227,6 @@ void Codegen::Reference::storeAccumulator() const
store.index = elementSubscript.stackSlot();
codegen->bytecodeGenerator->addTracingInstruction(store);
} return;
- case QmlScopeObject: {
- Instruction::StoreScopeObjectProperty store;
- store.base = qmlBase;
- store.propertyIndex = qmlCoreIndex;
- codegen->bytecodeGenerator->addInstruction(store);
- } return;
- case QmlContextObject: {
- Instruction::StoreContextObjectProperty store;
- store.base = qmlBase;
- store.propertyIndex = qmlCoreIndex;
- codegen->bytecodeGenerator->addInstruction(store);
- } return;
case Invalid:
case Accumulator:
case Const:
@@ -4396,9 +4341,15 @@ QT_WARNING_POP
}
}
if (!disable_lookups && global) {
- Instruction::LoadGlobalLookup load;
- load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
- codegen->bytecodeGenerator->addTracingInstruction(load);
+ if (qmlGlobal) {
+ Instruction::LoadQmlContextPropertyLookup load;
+ load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } else {
+ Instruction::LoadGlobalLookup load;
+ load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ }
} else {
Instruction::LoadName load;
load.name = nameAsIndex();
@@ -4432,24 +4383,6 @@ QT_WARNING_POP
load.base = elementBase;
codegen->bytecodeGenerator->addTracingInstruction(load);
} return;
- case QmlScopeObject: {
- Instruction::LoadScopeObjectProperty load;
- load.base = qmlBase;
- load.propertyIndex = qmlCoreIndex;
- load.captureRequired = capturePolicy == CaptureAtRuntime;
- codegen->bytecodeGenerator->addInstruction(load);
- if (capturePolicy == CaptureAheadOfTime)
- codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
- } return;
- case QmlContextObject: {
- Instruction::LoadContextObjectProperty load;
- load.base = qmlBase;
- load.propertyIndex = qmlCoreIndex;
- load.captureRequired = capturePolicy == CaptureAtRuntime;
- codegen->bytecodeGenerator->addInstruction(load);
- if (capturePolicy == CaptureAheadOfTime)
- codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
- } return;
case Invalid:
break;
}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 4d7001fe64..ad86483132 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -184,16 +184,31 @@ public:
Member,
Subscript,
Import,
- QmlScopeObject,
- QmlContextObject,
- LastLValue = QmlContextObject,
+ LastLValue = Import,
Const
} type = Invalid;
bool isLValue() const { return !isReadonly && type > Accumulator; }
- Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {}
- Reference(): constant(0) {}
+ Reference(Codegen *cg, Type t = Invalid) : Reference()
+ {
+ type = t;
+ codegen = cg;
+ }
+
+ Reference() :
+ constant(0),
+ isArgOrEval(false),
+ isReadonly(false),
+ isReferenceToConst(false),
+ requiresTDZCheck(false),
+ subscriptRequiresTDZCheck(false),
+ stackSlotIsLocalOrArgument(false),
+ isVolatile(false),
+ global(false),
+ qmlGlobal(false)
+ {}
+
Reference(const Reference &) = default;
Reference(Reference &&) = default;
Reference &operator =(const Reference &) = default;
@@ -206,10 +221,6 @@ public:
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
switch (type) {
- case QmlScopeObject:
- return capturePolicy != DontCapture;
- case QmlContextObject:
- return capturePolicy != DontCapture;
case Name:
case Member:
case Subscript:
@@ -228,28 +239,6 @@ public:
return isStackSlot();
}
- enum PropertyCapturePolicy {
- /*
- We're reading a property from the scope or context object, but it's a CONSTANT property,
- so we don't need to register a dependency at all.
- */
- DontCapture,
- /*
- We're reading the property of a QObject, and we know that it's the
- scope object or context object, which we know very well. Instead of registering a
- property capture every time, we can do that ahead of time and then register all those
- captures in one shot in registerQmlDependencies().
- */
- CaptureAheadOfTime,
- /*
- We're reading the property of a QObject, and we're not quite sure where
- the QObject comes from or what it is. So, when reading that property at run-time,
- make sure that we capture where we read that property so that if it changes we can
- re-evaluate the entire expression.
- */
- CaptureAtRuntime
- };
-
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
@@ -316,22 +305,6 @@ public:
r.isReadonly = true;
return r;
}
- static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
- Reference r(base.codegen, QmlScopeObject);
- r.qmlBase = base.storeOnStack().stackSlot();
- r.qmlCoreIndex = coreIndex;
- r.qmlNotifyIndex = notifyIndex;
- r.capturePolicy = capturePolicy;
- return r;
- }
- static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
- Reference r(base.codegen, QmlContextObject);
- r.qmlBase = base.storeOnStack().stackSlot();
- r.qmlCoreIndex = coreIndex;
- r.qmlNotifyIndex = notifyIndex;
- r.capturePolicy = capturePolicy;
- return r;
- }
static Reference fromThis(Codegen *cg) {
Reference r = fromStackSlot(cg, CallData::This);
r.isReadonly = true;
@@ -386,25 +359,21 @@ public:
Moth::StackSlot elementBase;
RValue elementSubscript;
};
- struct { // QML scope/context object case
- Moth::StackSlot qmlBase;
- qint16 qmlCoreIndex;
- qint16 qmlNotifyIndex;
- PropertyCapturePolicy capturePolicy;
- };
Moth::StackSlot property; // super property
};
QString name;
- mutable bool isArgOrEval = false;
- bool isReadonly = false;
- bool isReferenceToConst = false;
- bool requiresTDZCheck = false;
- bool subscriptRequiresTDZCheck = false;
- bool stackSlotIsLocalOrArgument = false;
- bool isVolatile = false;
- bool global = false;
Codegen *codegen = nullptr;
+ quint32 isArgOrEval:1;
+ quint32 isReadonly:1;
+ quint32 isReferenceToConst:1;
+ quint32 requiresTDZCheck:1;
+ quint32 subscriptRequiresTDZCheck:1;
+ quint32 stackSlotIsLocalOrArgument:1;
+ quint32 isVolatile:1;
+ quint32 global:1;
+ quint32 qmlGlobal:1;
+
private:
void storeAccumulator() const;
Reference doStoreOnStack(int tempIndex) const;
@@ -499,6 +468,10 @@ protected:
void setResult(const Reference &result) {
_result = result;
}
+
+ void setResult(Reference &&result) {
+ _result = std::move(result);
+ }
};
void enterContext(AST::Node *node);
@@ -532,6 +505,7 @@ public:
int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); }
int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); }
int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); }
+ int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); }
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
@@ -544,9 +518,22 @@ protected:
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
- Reference expression(AST::ExpressionNode *ast);
- void accept(AST::Node *node);
+ inline Reference expression(AST::ExpressionNode *ast)
+ {
+ if (!ast || hasError)
+ return Reference();
+
+ pushExpr();
+ ast->accept(this);
+ return popResult();
+ }
+
+ inline void accept(AST::Node *node)
+ {
+ if (!hasError && node)
+ node->accept(this);
+ }
void program(AST::Program *ast);
void statementList(AST::StatementList *ast);
@@ -561,12 +548,6 @@ protected:
Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
- // Hooks provided to implement QML lookup semantics
- virtual bool canAccelerateGlobalLookups() const { return true; }
- virtual Reference fallbackNameLookup(const QString &name);
-
- virtual void beginFunctionBodyHook() {}
-
void emitReturn(const Reference &expr);
// nodes
@@ -670,6 +651,11 @@ protected:
bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
+ void throwRecursionDepthError() override
+ {
+ throwSyntaxError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
public:
QList<DiagnosticMessage> errors() const;
@@ -684,6 +670,7 @@ public:
void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast);
void createTemplateObject(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
@@ -714,13 +701,40 @@ public:
m_globalNames = globalNames;
}
+ static const char *s_globalNames[];
protected:
friend class ScanFunctions;
friend struct ControlFlow;
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
- Result _expr;
+
+ inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); }
+ inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); }
+ inline Reference exprResult() const { return m_expressions.back().result(); }
+
+ inline bool exprAccept(Format f) { return m_expressions.back().accept(f); }
+
+ inline const Result &currentExpr() const { return m_expressions.back(); }
+
+ inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); }
+ inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); }
+ inline void pushExpr() { m_expressions.emplace_back(); }
+
+ inline Result popExpr()
+ {
+ const Result result = m_expressions.back();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ inline Reference popResult() {
+ const Reference result = m_expressions.back().result();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ std::vector<Result> m_expressions;
VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
@@ -769,33 +783,8 @@ protected:
bool _onoff;
};
- class RecursionDepthCheck {
- public:
- RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc)
- : _cg(cg)
- {
-#ifdef QT_NO_DEBUG
- const int depthLimit = 4000; // limit to ~1000 deep
-#else
- const int depthLimit = 1000; // limit to ~250 deep
-#endif // QT_NO_DEBUG
-
- ++_cg->_recursionDepth;
- if (_cg->_recursionDepth > depthLimit)
- _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded"));
- }
-
- ~RecursionDepthCheck()
- { --_cg->_recursionDepth; }
-
- private:
- Codegen *_cg;
- };
- int _recursionDepth = 0;
- friend class RecursionDepthCheck;
-
private:
- VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast);
void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 5dd6fca023..a78094f17c 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -50,6 +50,8 @@
#include <private/qqmlengine_p.h>
#include <private/qv4vme_moth_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
#include "qv4compilationunitmapper_p.h"
#include <QQmlPropertyMap>
#include <QDateTime>
@@ -95,18 +97,25 @@ CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName,
setUnitData(unitData, nullptr, fileName, finalUrlString);
}
-#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
{
+#ifndef V4_BOOTSTRAP
unlink();
+#endif
if (data) {
if (data->qmlUnit() != qmlData)
free(const_cast<QmlUnit *>(qmlData));
qmlData = nullptr;
+#ifndef V4_BOOTSTRAP
if (!(data->flags & QV4::CompiledData::Unit::StaticData))
free(const_cast<Unit *>(data));
+#else
+ // Unconditionally free the memory. In the dev tools we create units that have
+ // the flag set and will be saved to disk, so intended to persist later.
+ free(const_cast<Unit *>(data));
+#endif
}
data = nullptr;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
@@ -117,6 +126,7 @@ CompilationUnit::~CompilationUnit()
delete [] imports;
imports = nullptr;
}
+#ifndef V4_BOOTSTRAP
QString CompilationUnit::localCacheFilePath(const QUrl &url)
{
@@ -167,6 +177,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->setter = QV4::Lookup::setterGeneric;
else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
+ else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
l->nameIndex = compiledLookups[i].nameIndex;
}
}
@@ -269,6 +281,24 @@ void CompilationUnit::unlink()
propertyCaches.clear();
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i) {
+ QV4::Lookup &l = runtimeLookups[i];
+ if (l.getter == QV4::QObjectWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) {
+ if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
+ pc->release();
+ }
+
+ if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) {
+ if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
+ pc->release();
+ }
+ }
+ }
+
dependentScripts.clear();
typeNameCache = nullptr;
@@ -440,8 +470,10 @@ Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
ScopedString importName(scope);
const uint importCount = data->importEntryTableSize;
- imports = new const Value *[importCount];
- memset(imports, 0, importCount * sizeof(Value *));
+ if (importCount > 0) {
+ imports = new const Value *[importCount];
+ memset(imports, 0, importCount * sizeof(Value *));
+ }
for (uint i = 0; i < importCount; ++i) {
const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
@@ -867,6 +899,8 @@ bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engi
hash->addData(createPropertyCache(engine)->checksum(&ok));
return ok;
}
+ if (!compilationUnit)
+ return false;
hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum));
return true;
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1aaba13241..4cfd2d86e8 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -73,7 +73,13 @@
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x1b
+//
+// IMPORTANT:
+//
+// Also change the comment behind the number to describe the latest change. This has the added
+// benefit that if another patch changes the version too, it will result in a merge conflict, and
+// not get removed silently.
+#define QV4_DATA_STRUCTURE_VERSION 0x22 // Add trace slot to UPlus
class QIODevice;
class QQmlPropertyData;
@@ -159,9 +165,10 @@ static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected
struct Lookup
{
enum Type : unsigned int {
- Type_Getter = 0x0,
- Type_Setter = 0x1,
- Type_GlobalGetter = 2
+ Type_Getter = 0,
+ Type_Setter = 1,
+ Type_GlobalGetter = 2,
+ Type_QmlContextPropertyGetter = 3
};
union {
@@ -286,29 +293,16 @@ struct Function
quint16_le nRegisters;
Location location;
- // Qml Extensions Begin
- // Array of resolved ID objects
- size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
- quint16_le nDependingIdObjects;
- quint16_le nDependingContextProperties;
- // Array of int pairs (property index and notify index)
- size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); }
- quint16_le nDependingScopeProperties;
- // Array of int pairs (property index and notify index)
- size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); }
- // Qml Extensions End
+ quint32_le nLabelInfos;
+ size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
typedef quint16_le TraceInfoCount;
TraceInfoCount nTraceInfos;
static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
- quint32_le nLabelInfos;
- size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; }
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
- quint16 padding2;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
@@ -316,9 +310,6 @@ struct Function
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
- const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset()); }
- const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset()); }
- const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset()); }
// --- QQmlPropertyCacheCreator interface
const quint32_le *formalsBegin() const { return formalsTable(); }
@@ -329,11 +320,9 @@ struct Function
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
- inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
-
- static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) {
- int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize +
- 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
+ static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
+ int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
+ + nLines*sizeof(CodeOffsetToLine);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
return int(size);
@@ -343,7 +332,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 60, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
@@ -1106,11 +1095,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
const QmlUnit *qmlData = nullptr;
public:
CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString());
-#ifdef V4_BOOTSTRAP
- ~CompilationUnit() {}
-#else
~CompilationUnit();
-#endif
void addref()
{
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 0833f552e6..01c033cb2a 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -154,15 +154,19 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
- return registerGlobalGetterLookup(registerString(name));
+ CompiledData::Lookup l;
+ l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
+ l.nameIndex = nameIndex;
+ lookups << l;
+ return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
+int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
+ l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter;
l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
@@ -423,28 +427,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->nTraceInfos = irFunction->nTraceInfos;
function->nRegisters = irFunction->registerCountInFunction;
- function->nDependingIdObjects = 0;
- function->nDependingContextProperties = 0;
- function->nDependingScopeProperties = 0;
-
- if (!irFunction->idObjectDependencies.isEmpty()) {
- function->nDependingIdObjects = irFunction->idObjectDependencies.count();
- Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset);
- currentOffset += function->nDependingIdObjects * sizeof(quint32);
- }
-
- if (!irFunction->contextObjectPropertyDependencies.isEmpty()) {
- function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count();
- Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset);
- currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2;
- }
-
- if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) {
- function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count();
- Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset);
- currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
- }
-
if (!irFunction->labelInfo.empty()) {
function->nLabelInfos = quint32(irFunction->labelInfo.size());
Q_ASSERT(function->labelInfosOffset() == currentOffset);
@@ -470,25 +452,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
// write line numbers
memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
- // write QML dependencies
- quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset());
- for (int id : irFunction->idObjectDependencies) {
- Q_ASSERT(id >= 0);
- *writtenDeps++ = static_cast<quint32>(id);
- }
-
- writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset());
- for (auto property : irFunction->contextObjectPropertyDependencies) {
- *writtenDeps++ = property.key(); // property index
- *writtenDeps++ = property.value(); // notify index
- }
-
- writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset());
- for (auto property : irFunction->scopeObjectPropertyDependencies) {
- *writtenDeps++ = property.key(); // property index
- *writtenDeps++ = property.value(); // notify index
- }
-
quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
for (unsigned u : irFunction->labelInfo) {
*labels++ = u;
@@ -690,10 +653,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
Context *f = module->functions.at(i);
blockAndFunctionOffsets[i] = nextOffset;
- const int qmlIdDepsCount = f->idObjectDependencies.count();
- const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
- qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size());
+ int(f->labelInfo.size()), f->code.size());
functionSize += size - f->code.size();
nextOffset += size;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 2f5889ab53..49e334bb81 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -118,8 +118,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerGetterLookup(int nameIndex);
int registerSetterLookup(const QString &name);
int registerSetterLookup(int nameIndex);
- int registerGlobalGetterLookup(const QString &name);
int registerGlobalGetterLookup(int nameIndex);
+ int registerQmlContextPropertyGetterLookup(int nameIndex);
int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 931759fa72..52215c2ce6 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -187,10 +187,13 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS
}
// ### can we relax the restrictions here?
- if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ if (c->contextType == ContextType::Eval)
return result;
- result.type = ResolvedName::Global;
+ if (c->contextType == ContextType::Binding || c->contextType == ContextType::ScriptImportedByQML)
+ result.type = ResolvedName::QmlGlobal;
+ else
+ result.type = ResolvedName::Global;
return result;
}
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 5b91b93346..57ef4be36e 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -217,7 +217,6 @@ struct Context {
bool usesThis = false;
bool innerFunctionAccessesThis = false;
bool innerFunctionAccessesNewTarget = false;
- bool hasTry = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
bool requiresExecutionContext = false;
@@ -278,11 +277,6 @@ struct Context {
}
};
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
Context(Context *parent, ContextType type)
: parent(parent)
, contextType(type)
@@ -338,6 +332,7 @@ struct Context {
struct ResolvedName {
enum Type {
Unresolved,
+ QmlGlobal,
Global,
Local,
Stack,
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index e0eaa8867b..8984b6931e 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -57,7 +57,8 @@ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
- : _cg(cg)
+ : QQmlJS::AST::Visitor(cg->recursionDepth())
+ , _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
@@ -96,25 +97,6 @@ void ScanFunctions::leaveEnvironment()
_context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
}
-bool ScanFunctions::preVisit(Node *ast)
-{
- if (_cg->hasError)
- return false;
- ++_recursionDepth;
-
- if (_recursionDepth > 1000) {
- _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded"));
- return false;
- }
-
- return true;
-}
-
-void ScanFunctions::postVisit(Node *)
-{
- --_recursionDepth;
-}
-
void ScanFunctions::checkDirectivePrologue(StatementList *ast)
{
for (StatementList *it = ast; it; it = it->next) {
@@ -759,9 +741,9 @@ void ScanFunctions::calcEscapingVariables()
if (c->contextType != ContextType::ESModule)
continue;
for (const auto &entry: c->exportEntries) {
- auto m = c->members.find(entry.localName);
- if (m != c->members.end())
- m->canEscape = true;
+ auto mIt = c->members.constFind(entry.localName);
+ if (mIt != c->members.constEnd())
+ mIt->canEscape = true;
}
break;
}
@@ -777,8 +759,8 @@ void ScanFunctions::calcEscapingVariables()
}
Q_ASSERT(c != inner);
while (c) {
- Context::MemberMap::const_iterator it = c->members.find(var);
- if (it != c->members.end()) {
+ Context::MemberMap::const_iterator it = c->members.constFind(var);
+ if (it != c->members.constEnd()) {
if (c->parent || it->isLexicallyScoped()) {
it->canEscape = true;
c->requiresExecutionContext = true;
@@ -834,15 +816,17 @@ void ScanFunctions::calcEscapingVariables()
// add an escaping 'this' variable
c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let);
c->requiresExecutionContext = true;
- auto m = c->members.find(QStringLiteral("this"));
- m->canEscape = true;
+ auto mIt = c->members.constFind(QStringLiteral("this"));
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
if (c->innerFunctionAccessesNewTarget) {
// add an escaping 'new.target' variable
c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
c->requiresExecutionContext = true;
- auto m = c->members.find(QStringLiteral("new.target"));
- m->canEscape = true;
+ auto mIt = c->members.constFind(QStringLiteral("new.target"));
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
c->allVarsEscape = false;
@@ -863,8 +847,9 @@ void ScanFunctions::calcEscapingVariables()
}
if (c->contextType == ContextType::Block && c->isCatchBlock) {
c->requiresExecutionContext = true;
- auto m = c->members.find(c->caughtVariable);
- m->canEscape = true;
+ auto mIt = c->members.constFind(c->caughtVariable);
+ Q_ASSERT(mIt != c->members.constEnd());
+ mIt->canEscape = true;
}
const QLatin1String exprForOn("expression for on");
if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() &&
@@ -873,7 +858,7 @@ void ScanFunctions::calcEscapingVariables()
// we don't know if the code is a signal handler or not.
c->requiresExecutionContext = true;
if (c->allVarsEscape) {
- for (auto &m : c->members)
+ for (const auto &m : qAsConst(c->members))
m.canEscape = true;
}
}
@@ -893,3 +878,8 @@ void ScanFunctions::calcEscapingVariables()
}
}
}
+
+void ScanFunctions::throwRecursionDepthError()
+{
+ _cg->throwRecursionDepthError();
+}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 28ad846bcd..0f7bf1818a 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -96,9 +96,6 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- bool preVisit(AST::Node *ast) override;
- void postVisit(AST::Node *) override;
-
void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
@@ -160,6 +157,8 @@ protected:
bool visit(AST::WithStatement *ast) override;
void endVisit(AST::WithStatement *ast) override;
+ void throwRecursionDepthError() override;
+
protected:
bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
@@ -173,8 +172,6 @@ protected:
bool _allowFuncDecls;
ContextType defaultProgramType;
- unsigned _recursionDepth = 0;
-
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
};
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index c0b1be1492..b019f191fa 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -287,6 +287,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << TRACE_SLOT;
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ d << index << TRACE_SLOT;
+ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(StoreNameSloppy)
d << name;
MOTH_END_INSTR(StoreNameSloppy)
@@ -328,26 +332,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(property, nFormals);
MOTH_END_INSTR(StoreSuperProperty)
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- d << dumpRegister(base, nFormals) << "[" << index << "]";
- MOTH_END_INSTR(LoadIdObject)
-
MOTH_BEGIN_INSTR(Yield)
MOTH_END_INSTR(Yield)
@@ -394,15 +378,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
MOTH_END_INSTR(CallGlobalLookup)
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
- << TRACE_SLOT;
- MOTH_END_INSTR(CallContextObjectProperty)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
+ d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
@@ -615,6 +593,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(UNot)
MOTH_BEGIN_INSTR(UPlus)
+ d << TRACE_SLOT;
MOTH_END_INSTR(UPlus)
MOTH_BEGIN_INSTR(UMinus)
@@ -729,14 +708,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index;
MOTH_END_INSTR(GetTemplateObject)
- MOTH_BEGIN_INSTR(LoadQmlContext)
- d << dumpRegister(result, nFormals);
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- d << dumpRegister(result, nFormals);
- MOTH_END_INSTR(LoadQmlImportedScripts)
-
MOTH_BEGIN_INSTR(TailCall)
d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(TailCall)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 3996143843..6a8c9a9549 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -86,12 +86,11 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value)
#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot)
#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot)
+#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot)
#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot)
#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot)
-#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
-#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base)
#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0)
#define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0)
@@ -101,8 +100,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property)
#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property)
-#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
-#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot)
#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot)
#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot)
@@ -113,8 +110,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot)
#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot)
#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot)
-#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot)
-#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, argc, argv, traceSlot)
+#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot)
#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot)
#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
@@ -171,7 +167,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
-#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
+#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 1, traceSlot)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot)
#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0)
#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot)
@@ -194,7 +190,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot)
#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot)
-#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0)
@@ -228,6 +223,7 @@ QT_BEGIN_NAMESPACE
F(LoadClosure) \
F(LoadName) \
F(LoadGlobalLookup) \
+ F(LoadQmlContextPropertyLookup) \
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
@@ -238,11 +234,6 @@ QT_BEGIN_NAMESPACE
F(SetLookup) \
F(LoadSuperProperty) \
F(StoreSuperProperty) \
- F(StoreScopeObjectProperty) \
- F(StoreContextObjectProperty) \
- F(LoadScopeObjectProperty) \
- F(LoadContextObjectProperty) \
- F(LoadIdObject) \
F(ConvertThisToObject) \
F(ToObject) \
F(Jump) \
@@ -296,8 +287,7 @@ QT_BEGIN_NAMESPACE
F(CallName) \
F(CallPossiblyDirectEval) \
F(CallGlobalLookup) \
- F(CallScopeObjectProperty) \
- F(CallContextObjectProperty) \
+ F(CallQmlContextPropertyLookup) \
F(CallWithSpread) \
F(Construct) \
F(ConstructWithSpread) \
@@ -328,8 +318,6 @@ QT_BEGIN_NAMESPACE
F(CreateMappedArgumentsObject) \
F(CreateUnmappedArgumentsObject) \
F(CreateRestParameter) \
- F(LoadQmlContext) \
- F(LoadQmlImportedScripts) \
F(Yield) \
F(YieldStar) \
F(Resume) \
diff --git a/src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml
new file mode 100644
index 0000000000..104a2209d7
--- /dev/null
+++ b/src/qml/doc/snippets/qml/tablemodel/fruit-example-complex.qml
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![file]
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import Qt.labs.qmlmodels 1.0
+
+Window {
+ width: 400
+ height: 400
+ visible: true
+
+ TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: TableModel {
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][0].checked }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][2].fruitType }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][2].fruitType = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][3].fruitName }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][3].fruitName = cellData }
+ }
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][4].fruitPrice }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][4].fruitPrice = cellData }
+ }
+
+ // Each row is one type of fruit that can be ordered
+//![rows]
+ rows: [
+ [
+ // Each object (line) is one cell/column.
+ { checked: false, checkable: true },
+ { amount: 1 },
+ { fruitType: "Apple" },
+ { fruitName: "Granny Smith" },
+ { fruitPrice: 1.50 }
+ ],
+ [
+ { checked: true, checkable: true },
+ { amount: 4 },
+ { fruitType: "Orange" },
+ { fruitName: "Navel" },
+ { fruitPrice: 2.50 }
+ ],
+ [
+ { checked: false, checkable: false },
+ { amount: 1 },
+ { fruitType: "Banana" },
+ { fruitName: "Cavendish" },
+ { fruitPrice: 3.50 }
+ ]
+ ]
+//![rows]
+ }
+//![delegate]
+ delegate: TextInput {
+ text: model.display
+ padding: 12
+ selectByMouse: true
+
+ onAccepted: model.display = text
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#efefef"
+ z: -1
+ }
+ }
+//![delegate]
+ }
+}
+//![file]
diff --git a/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
index 3d44f61668..d3f6176c70 100644
--- a/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
+++ b/src/qml/doc/snippets/qml/tablemodel/fruit-example-delegatechooser.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -65,32 +65,37 @@ ApplicationWindow {
boundsBehavior: Flickable.StopAtBounds
model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
// Each row is one type of fruit that can be ordered
//![rows]
rows: [
- [
- // Each object (line) is one cell/column,
- // and each property in that object is a role.
- { checked: false, checkable: true },
- { amount: 1 },
- { fruitType: "Apple" },
- { fruitName: "Granny Smith" },
- { fruitPrice: 1.50 }
- ],
- [
- { checked: true, checkable: true },
- { amount: 4 },
- { fruitType: "Orange" },
- { fruitName: "Navel" },
- { fruitPrice: 2.50 }
- ],
- [
- { checked: false, checkable: true },
- { amount: 1 },
- { fruitType: "Banana" },
- { fruitName: "Cavendish" },
- { fruitPrice: 3.50 }
- ]
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
]
//![rows]
}
@@ -99,15 +104,15 @@ ApplicationWindow {
DelegateChoice {
column: 0
delegate: CheckBox {
- checked: model.checked
- onToggled: model.checked = checked
+ checked: model.display
+ onToggled: model.display = checked
}
}
DelegateChoice {
column: 1
delegate: SpinBox {
- value: model.amount
- onValueModified: model.amount = value
+ value: model.display
+ onValueModified: model.display = value
}
}
DelegateChoice {
diff --git a/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml b/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
index 5f00eb484b..f51c1818c3 100644
--- a/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
+++ b/src/qml/doc/snippets/qml/tablemodel/fruit-example-simpledelegate.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -65,32 +65,37 @@ Window {
boundsBehavior: Flickable.StopAtBounds
model: TableModel {
+ TableModelColumn { display: "checked" }
+ TableModelColumn { display: "amount" }
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitName" }
+ TableModelColumn { display: "fruitPrice" }
+
// Each row is one type of fruit that can be ordered
//![rows]
rows: [
- [
- // Each object (line) is one cell/column,
- // and each property in that object is a role.
- { checked: false, checkable: true },
- { amount: 1 },
- { fruitType: "Apple" },
- { fruitName: "Granny Smith" },
- { fruitPrice: 1.50 }
- ],
- [
- { checked: true, checkable: true },
- { amount: 4 },
- { fruitType: "Orange" },
- { fruitName: "Navel" },
- { fruitPrice: 2.50 }
- ],
- [
- { checked: false, checkable: true },
- { amount: 1 },
- { fruitType: "Banana" },
- { fruitName: "Cavendish" },
- { fruitPrice: 3.50 }
- ]
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ {
+ checked: true,
+ amount: 4,
+ fruitType: "Orange",
+ fruitName: "Navel",
+ fruitPrice: 2.50
+ },
+ {
+ checked: false,
+ amount: 1,
+ fruitType: "Banana",
+ fruitName: "Cavendish",
+ fruitPrice: 3.50
+ }
]
//![rows]
}
@@ -100,7 +105,6 @@ Window {
padding: 12
selectByMouse: true
- // TODO: the property used here is undefined
onAccepted: model.display = text
Rectangle {
diff --git a/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml b/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml
deleted file mode 100644
index 63978a370d..0000000000
--- a/src/qml/doc/snippets/qml/tablemodel/roleDataProvider.qml
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-//![0]
-import QtQuick 2.12
-import Qt.labs.qmlmodels 1.0
-
-TableView {
- columnSpacing: 1; rowSpacing: 1
- model: TableModel {
- property real taxPercent: 10
- rows: [
- [{ fruitType: "Apple" }, { fruitPrice: 1.50 }],
- [{ fruitType: "Orange" }, { fruitPrice: 2.50 }]
- ]
- roleDataProvider: function(index, role, cellData) {
- if (role === "display") {
- if (cellData.hasOwnProperty("fruitPrice")) {
- console.log("row", index.row, "taxing your fruit", JSON.stringify(cellData))
- return (cellData.fruitPrice * (1 + taxPercent / 100)).toFixed(2);
- }
- else if (cellData.hasOwnProperty("fruitType"))
- return cellData.fruitType;
- }
- return cellData;
- }
- }
- delegate: Rectangle {
- implicitWidth: 150; implicitHeight: 30
- color: "lightsteelblue"
- Text {
- x: 6; anchors.verticalCenter: parent.verticalCenter
- text: display
- }
- }
-}
-//![0]
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index f6f630c749..41bc9fd140 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -142,9 +142,10 @@ types:
\li qmlRegisterType() (with no parameters) registers a C++ type that is not
instantiable and cannot be referred to from QML. This enables the engine to
coerce any inherited types that are instantiable from QML.
-\li qmlRegisterInterface() registers a Qt interface type with a specific QML
-type name. The type is not instantiable from QML but can be referred to by its
-type name.
+\li qmlRegisterInterface() registers an existing Qt interface type. The type is
+not instantiable from QML, and you cannot declare QML properties with it. Using
+C++ properties of this type from QML will do the expected interface casts,
+though.
\li qmlRegisterUncreatableType() registers a named C++ type that is not
instantiable but should be identifiable as a type to the QML type system. This
is useful if a type's enums or attached properties should be accessible from QML
diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc
index 24ff640284..7a6a922480 100644
--- a/src/qml/doc/src/javascript/functionlist.qdoc
+++ b/src/qml/doc/src/javascript/functionlist.qdoc
@@ -55,6 +55,8 @@
\li decodeURIComponent(encodedURIComponent)
\li encodeURI(uri)
\li encodeURIComponent(uriComponent)
+ \li escape(string)
+ \li unescape(string)
\endlist
\section2 Constructor Properties
@@ -63,11 +65,20 @@
\li Object
\li Function
\li Array
+ \li ArrayBuffer
\li String
\li Boolean
\li Number
+ \li DataView
\li Date
+ \li Promise
\li RegExp
+ \li Map
+ \li WeakMap
+ \li Set
+ \li WeakSet
+ \li SharedArrayBuffer
+ \li Symbol
\li Error
\li EvalError
\li RangeError
@@ -80,8 +91,11 @@
\section2 Other Properties
\list
+ \li Atomics
\li Math
\li JSON
+ \li Reflect
+ \li Proxy
\endlist
\section1 The Object Object
@@ -92,12 +106,19 @@
\list
\li getPrototypeOf(O)
+ \li setPrototypeOf(O, P)
\li getOwnPropertyDescriptor(O, P)
+ \li getOwnPropertyDescriptors(O)
\li getOwnPropertyNames(O)
+ \li getOwnPropertySymbols(O)
+ \li assign(O [, Properties])
\li create(O [, Properties])
\li defineProperty(O, P, Attributes)
\li defineProperties(O, Properties)
+ \li entries(O)
+ \li is(V1, V2)
\li keys(O)
+ \li values(O)
\li seal(O)
\li isSealed(O)
\li freeze(O)
@@ -117,6 +138,8 @@
\li hasOwnProperty(V)
\li isPrototypeOf(V)
\li propertyIsEnumerable(V)
+ \li __defineGetter__(P, F)
+ \li __defineSetter__(P, F)
\endlist
\section1 Function Objects
@@ -130,6 +153,7 @@
\li apply(thisArg, argArray)
\li call(thisArg [, arg1 [, arg2, ...]])
\li bind((thisArg [, arg1 [, arg2, …]])
+ \li [Symbol.hasInstance](O)
\endlist
\section1 Array Objects
@@ -142,9 +166,14 @@
\li toString()
\li toLocaleString()
\li concat([item1 [, item2 [, ...]]])
+ \li copyWithin([item1 [, item2 [, ...]]])
+ \li entries()
+ \li fill(item [, index1 [, index2]])
\li join(separator)
\li find(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
\li findIndex(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
+ \li includes(item)
+ \li keys()
\li pop()
\li push([item1 [, item2 [, ...]]])
\li reverse()
@@ -162,6 +191,8 @@
\li filter(callbackfn [, thisArg])
\li reduce(callbackfn [, initialValue])
\li reduceRight(callbackfn [, initialValue])
+ \li values()
+ \li [Symbol.iterator]()
\endlist
\section1 String Objects
@@ -175,6 +206,7 @@
\li valueOf()
\li charAt(pos)
\li charCodeAt(pos)
+ \li codePointAt(pos)
\li concat([string1 [, string2 [, ...]]])
\li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8
\li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8
@@ -182,18 +214,23 @@
\li lastIndexOf(searchString, position)
\li localeCompare(that)
\li match(regexp)
+ \li normalize()
+ \li padEnd(length [, string])
+ \li padStart(length [, string])
\li repeat(count) // ECMAScript 6: Added in Qt 5.9
\li replace(searchValue, replaceValue)
\li search(regexp)
\li slice(start, end)
\li split(separator, limit)
\li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8
+ \li substr(start, length)
\li substring(start, end)
\li toLowerCase()
\li toLocaleLowerCase()
\li toUpperCase()
\li toLocaleUpperCase()
\li trim()
+ \li [Symbol.iterator]()
\endlist
Additionally, the QML engine adds the following functions to the \l String prototype:
@@ -222,6 +259,7 @@
\list
\li toString(radix)
\li toLocaleString()
+ \li valueOf()
\li toFixed(fractionDigits)
\li toExponential(fractionDigits)
\li toPrecision(precision)
@@ -245,12 +283,16 @@
\li MAX_VALUE
\li MIN_VALUE
\li EPSILON // ECMAScript 6: Added in Qt 5.8
+ \li MAX_SAFE_INTEGER
+ \li MIN_SAFE_INTEGER
\endlist
\section3 Function Properties
\list
\li isFinite(x) // ECMAScript 6: Added in Qt 5.8
+ \li isInteger(x)
+ \li isSafeInteger(x)
\li isNaN(x) // ECMAScript 6: Added in Qt 5.8
\endlist
@@ -274,14 +316,27 @@
\list
\li abs(x)
\li acos(x)
+ \li acosh(x)
\li asin(x)
+ \li asinh(x)
\li atan(x)
+ \li atanh(x)
\li atan2(y, x)
+ \li cbrt(x)
\li ceil(x)
+ \li clz32(x)
\li cos(x)
+ \li cosh(x)
\li exp(x)
+ \li expm1(x)
\li floor(x)
+ \li fround(x)
+ \li hypot(x, y)
+ \li imul(x, y)
\li log(x)
+ \li log10(x)
+ \li log1p(x)
+ \li log2(x)
\li max([value1 [, value2 [, ...]]])
\li min([value1 [, value2 [, ...]]])
\li pow(x, y)
@@ -289,8 +344,11 @@
\li round(x)
\li sign(x) // ECMAScript 6: Added in Qt 5.8
\li sin(x)
+ \li sinh(x)
\li sqrt(x)
\li tan(x)
+ \li tanh(x)
+ \li trunc(x)
\endlist
\section1 Date Objects
@@ -338,11 +396,14 @@
\li setUTCDate(date)
\li setMonth(month [, date])
\li setUTCMonth(month [, date])
+ \li setYear(year)
\li setFullYear(year [, month [, date]])
\li setUTCFullYear(year [, month [, date]])
\li toUTCString()
+ \li toGMTString()
\li toISOString()
\li toJSON()
+ \li [Symbol.toPrimitive](hint)
\endlist
Additionally, the QML engine adds the following functions to the \l Date prototype:
diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc
index eb40f10065..c22c392b80 100644
--- a/src/qml/doc/src/javascript/hostenvironment.qdoc
+++ b/src/qml/doc/src/javascript/hostenvironment.qdoc
@@ -40,11 +40,10 @@ not provide a \c window object or \c{DOM API} as commonly found in a browser env
Like a browser or server-side JavaScript environment, the QML runtime implements the
\l{ECMA-262}{ECMAScript Language Specification} standard. This provides access to
all of the built-in types and functions defined by the standard, such as Object, Array, and Math.
-The QML runtime implements the 5th edition of the standard, which is the same edition commonly
-implemented by browsers.
+The QML runtime implements the 7th edition of the standard.
The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more
-information on their use, please refer to the ECMA-262 5th edition standard or one of the many online
+information on their use, please refer to the ECMA-262 7th edition standard or one of the many online
JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects
Reference section). Many sites focus on JavaScript in the browser, so in some cases you may need to double
check the specification to determine whether a given function or object is part of standard ECMAScript or
diff --git a/src/qml/doc/src/javascript/number.qdoc b/src/qml/doc/src/javascript/number.qdoc
index b6f80f474a..288232255c 100644
--- a/src/qml/doc/src/javascript/number.qdoc
+++ b/src/qml/doc/src/javascript/number.qdoc
@@ -96,7 +96,7 @@
\code
var german = Qt.locale("de_DE");
var d;
- d = Number.fromLocaleString(german, "1234,56) // d == 1234.56
+ d = Number.fromLocaleString(german, "1234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1.234,56") // d == 1234.56
d = Number.fromLocaleString(german, "1234.56") // throws exception
d = Number.fromLocaleString(german, "1.234") // d == 1234.0
diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc
index 6986f1baa0..231b85af76 100644
--- a/src/qml/doc/src/statemachine.qdoc
+++ b/src/qml/doc/src/statemachine.qdoc
@@ -27,7 +27,7 @@
/*!
\qmlmodule QtQml.StateMachine 1.\QtMinorVersion
- \title Declarative State Machine QML Types
+ \title Qt QML State Machine QML Types
\brief Provides QML types to create and execute state graphs.
The following is a list of QML types provided by the module:
@@ -322,7 +322,7 @@
\section1 Related Information
\list
- \li \l{Declarative State Machine QML Types}
+ \li \l{Qt QML State Machine QML Types}
\li \l{The State Machine Framework}
\endlist
*/
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 49eb2e8a37..bc0d20dba7 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -18,7 +18,13 @@ SOURCES += \
$$PWD/qv4node.cpp \
$$PWD/qv4graph.cpp \
$$PWD/qv4graphbuilder.cpp \
+ $$PWD/qv4lowering.cpp \
$$PWD/qv4tracingjit.cpp \
+ $$PWD/qv4mi.cpp \
+ $$PWD/qv4domtree.cpp \
+ $$PWD/qv4schedulers.cpp \
+ $$PWD/qv4blockscheduler.cpp \
+ $$PWD/qv4loopinfo.cpp
HEADERS += \
$$PWD/qv4ir_p.h \
@@ -27,4 +33,11 @@ HEADERS += \
$$PWD/qv4node_p.h \
$$PWD/qv4graph_p.h \
$$PWD/qv4graphbuilder_p.h \
+ $$PWD/qv4lowering_p.h \
+ $$PWD/qv4mi_p.h \
+ $$PWD/qv4miblockset_p.h \
+ $$PWD/qv4domtree_p.h \
+ $$PWD/qv4schedulers_p.h \
+ $$PWD/qv4blockscheduler_p.h \
+ $$PWD/qv4loopinfo_p.h
}
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index 1b60e96f2c..238c11f478 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -208,17 +208,20 @@ public:
isNumber.link(this);
}
+ // this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
load64(lhs, lhsTarget);
urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
- pushAligned(AccumulatorRegister);
+ const Address accumulatorStackAddress(JSStackFrameRegister,
+ offsetof(CallData, accumulator));
+ storeAccumulator(accumulatorStackAddress);
move(lhsTarget, registerForArg(0));
callHelper(toInt32Helper);
move(ReturnValueRegister, lhsTarget);
- popAligned(AccumulatorRegister);
+ loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
@@ -498,6 +501,7 @@ public:
isNumber.link(this);
}
+ // this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
@@ -510,32 +514,28 @@ public:
auto lhsIsInt = jump();
lhsIsNotInt.link(this);
- if (accumulatorNeedsSaving) {
- push(AccumulatorRegisterTag);
- push(AccumulatorRegisterValue);
- }
+
+ // Save accumulator from being garbage collected, no matter if we will reuse the register.
+ const Address accumulatorStackAddress(JSStackFrameRegister,
+ offsetof(CallData, accumulator));
+ storeAccumulator(accumulatorStackAddress);
if (ArgInRegCount < 2) {
- if (!accumulatorNeedsSaving)
- subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(lhsTarget);
load32(lhs, lhsTarget);
push(lhsTarget);
} else {
- if (accumulatorNeedsSaving)
- subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
move(lhsTarget, registerForArg(1));
load32(lhs, registerForArg(0));
}
callHelper(toInt32Helper);
move(ReturnValueRegisterValue, lhsTarget);
- if (accumulatorNeedsSaving) {
- addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
- pop(AccumulatorRegisterValue);
- pop(AccumulatorRegisterTag);
- } else if (ArgInRegCount < 2) {
+ if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
- }
+
+ if (accumulatorNeedsSaving) // otherwise it's still the same
+ loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index f2f7a12598..80155d7b20 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -213,6 +213,14 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator);
}
+void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
+}
+
void BaselineJIT::generate_StoreNameSloppy(int name)
{
STORE_IP();
@@ -329,61 +337,6 @@ void BaselineJIT::generate_StoreSuperProperty(int property)
BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore);
}
-
-void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlScopeObjectProperty, CallResultDestination::Ignore);
-}
-
-void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlContextObjectProperty, CallResultDestination::Ignore);
-}
-
-void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlScopeObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_LoadIdObject(int index, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlIdObject, CallResultDestination::InAccumulator);
-}
-
void BaselineJIT::generate_Yield()
{
// #####
@@ -493,31 +446,18 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /
BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator);
}
-void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passJSSlotAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passJSSlotAsArg(base, 1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlScopeObjectProperty, CallResultDestination::InAccumulator);
-}
-
-void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
+void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
+ int /*traceSlot*/)
{
STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passJSSlotAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passJSSlotAsArg(base, 1);
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passJSSlotAsArg(argv, 2);
+ as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextObjectProperty, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
-
void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/)
{
STORE_IP();
@@ -891,7 +831,7 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
}
void BaselineJIT::generate_UNot() { as->unot(); }
-void BaselineJIT::generate_UPlus() { as->toNumber(); }
+void BaselineJIT::generate_UPlus(int /*traceSlot*/) { as->toNumber(); }
void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); }
void BaselineJIT::generate_UCompl() { as->ucompl(); }
void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); }
@@ -937,22 +877,6 @@ void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); }
// as->checkException();
//}
-void BaselineJIT::generate_LoadQmlContext(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContext, CallResultDestination::InAccumulator);
- as->storeReg(result);
-}
-
-void BaselineJIT::generate_LoadQmlImportedScripts(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlImportedScripts, CallResultDestination::InAccumulator);
- as->storeReg(result);
-}
-
void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
as->loadValue(Value::emptyValue().rawValue());
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 5f3cb7eb2e..37ab37eac2 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -97,6 +97,7 @@ public:
void generate_LoadClosure(int value) override;
void generate_LoadName(int name, int traceSlot) override;
void generate_LoadGlobalLookup(int index, int traceSlot) override;
+ void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override;
void generate_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
void generate_LoadElement(int base, int traceSlot) override;
@@ -107,15 +108,6 @@ public:
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
- void generate_StoreScopeObjectProperty(int base,
- int propertyIndex) override;
- void generate_StoreContextObjectProperty(int base,
- int propertyIndex) override;
- void generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadIdObject(int index, int base) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int) override;
@@ -128,8 +120,7 @@ public:
void generate_CallName(int name, int argc, int argv, int traceSlot) override;
void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
- void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override;
- void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override;
+ void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override;
void generate_TailCall(int func, int thisObject, int argc, int argv) override;
void generate_Construct(int func, int argc, int argv) override;
@@ -188,7 +179,7 @@ public:
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
- void generate_UPlus() override;
+ void generate_UPlus(int) override;
void generate_UMinus(int traceSlot) override;
void generate_UCompl() override;
void generate_Increment(int traceSlot) override;
@@ -211,8 +202,6 @@ public:
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
- void generate_LoadQmlContext(int result) override;
- void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
diff --git a/src/qml/jit/qv4blockscheduler.cpp b/src/qml/jit/qv4blockscheduler.cpp
new file mode 100644
index 0000000000..3e2bfe15c5
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4blockscheduler_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcBlockScheduler, "qt.v4.ir.blockscheduler")
+
+bool BlockScheduler::checkCandidate(MIBlock *candidate)
+{
+ Q_ASSERT(loopInfo.loopHeaderFor(candidate) == currentGroup.group);
+
+ for (MIBlock *pred : candidate->inEdges()) {
+ if (pred->isDeoptBlock())
+ continue;
+
+ if (emitted.alreadyProcessed(pred))
+ continue;
+
+ if (dominatorTree.dominates(candidate->index(), pred->index())) {
+ // this is a loop, where there in
+ // -> candidate edge is the jump back to the top of the loop.
+ continue;
+ }
+
+ if (pred == candidate)
+ // this is a very tight loop, e.g.:
+ // L1: ...
+ // goto L1
+ // This can happen when, for example, the basic-block merging gets rid of the empty
+ // body block. In this case, we can safely schedule this block (if all other
+ // incoming edges are either loop-back edges, or have been scheduled already).
+ continue;
+
+ return false; // an incoming edge that is not yet emitted, and is not a back-edge
+ }
+
+ if (loopInfo.isLoopHeader(candidate)) {
+ // postpone everything, and schedule the loop first.
+ postponedGroups.push(currentGroup);
+ currentGroup = WorkForGroup(candidate);
+ }
+
+ return true;
+}
+
+MIBlock *BlockScheduler::pickNext()
+{
+ while (true) {
+ while (currentGroup.postponed.isEmpty()) {
+ if (postponedGroups.isEmpty())
+ return nullptr;
+ if (currentGroup.group) // record the first and the last node of a group
+ loopsStartEnd[currentGroup.group] = sequence.back();
+ currentGroup = postponedGroups.pop();
+ }
+
+ MIBlock *next = currentGroup.postponed.pop();
+ if (checkCandidate(next))
+ return next;
+ }
+
+ Q_UNREACHABLE();
+ return nullptr;
+}
+
+void BlockScheduler::emitBlock(MIBlock *bb)
+{
+ if (emitted.alreadyProcessed(bb))
+ return;
+
+ sequence.push_back(bb);
+ emitted.markAsProcessed(bb);
+}
+
+void BlockScheduler::schedule(MIBlock *functionEntryPoint)
+{
+ MIBlock *next = functionEntryPoint;
+
+ while (next) {
+ emitBlock(next);
+ // postpone all outgoing edges, if they were not already processed
+ QVarLengthArray<MIBlock *, 32> nonExceptionEdges;
+ // first postpone all exception edges, so they will be processed last
+ for (int i = next->outEdges().size(); i != 0; ) {
+ --i;
+ MIBlock *out = next->outEdges().at(i);
+ if (emitted.alreadyProcessed(out))
+ continue;
+ if (out == nullptr)
+ continue;
+ if (out->instructions().front().opcode() == Meta::OnException)
+ postpone(out);
+ else
+ nonExceptionEdges.append(out);
+ }
+ for (MIBlock *edge : nonExceptionEdges)
+ postpone(edge);
+ next = pickNext();
+ }
+
+ // finally schedule all de-optimization blocks at the end
+ for (auto bb : dominatorTree.function()->blocks()) {
+ if (bb->isDeoptBlock())
+ emitBlock(bb);
+ }
+}
+
+void BlockScheduler::postpone(MIBlock *bb)
+{
+ if (currentGroup.group == loopInfo.loopHeaderFor(bb)) {
+ currentGroup.postponed.append(bb);
+ return;
+ }
+
+ for (int i = postponedGroups.size(); i != 0; ) {
+ --i;
+ WorkForGroup &g = postponedGroups[i];
+ if (g.group == loopInfo.loopHeaderFor(bb)) {
+ g.postponed.append(bb);
+ return;
+ }
+ }
+
+ Q_UNREACHABLE();
+}
+
+void BlockScheduler::dump() const
+{
+ if (!lcBlockScheduler().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Scheduled blocks:\n");
+ for (auto *bb : sequence) {
+ s += QLatin1String(" L") + QString::number(bb->index());
+ MIBlock *loopEnd = loopsStartEnd[bb];
+ if (loopEnd)
+ s += QLatin1String(", loop start, ends at L") + QString::number(loopEnd->index());
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcBlockScheduler).noquote().nospace() << s;
+}
+
+BlockScheduler::BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo)
+ : dominatorTree(dominatorTree)
+ , loopInfo(loopInfo)
+ , sequence(0)
+ , emitted(dominatorTree.function())
+{
+ schedule(dominatorTree.function()->blocks().front());
+
+ dump();
+
+ if (dominatorTree.function()->blockCount() != sequence.size()) {
+ qFatal("The block scheduler did not schedule all blocks. This is most likely due to"
+ "a non-natural loop.");
+ // Usually caused by having an execution path that manages to skip over unwind handler
+ // reset: any exception happening after will jump back to the unwind handler, and thereby
+ // creating a loop that can be entered in 2 different ways.
+ }
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4blockscheduler_p.h b/src/qml/jit/qv4blockscheduler_p.h
new file mode 100644
index 0000000000..1289fda9f0
--- /dev/null
+++ b/src/qml/jit/qv4blockscheduler_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4BLOCKSCHEDULER_P_H
+#define QV4BLOCKSCHEDULER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstack.h>
+
+#include "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+class LoopInfo;
+
+// High-level algorithm:
+// 0. start with the first node (the start node) of a function
+// 1. emit the node
+// 2. add all outgoing edges that are not yet emitted to the postponed stack
+// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
+// we're done.
+// 4. pop a node from the postponed stack, and check if it can be scheduled:
+// a. if all incoming edges are scheduled, go to 4.
+// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
+// back to the start of the loop), ignore it
+// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
+// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
+// 6. go back to 1.
+//
+// The postponing action in step 2 will put the node into its containing group. The case where this
+// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
+// outgoing edge points to a node that is not part of the current loop (and possibly not of the
+// parent loop).
+//
+// Linear scan register allocation benefits greatly from short life-time intervals with few holes
+// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
+// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
+// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
+// the same reason.
+class BlockScheduler
+{
+ const DominatorTree &dominatorTree;
+ const LoopInfo &loopInfo;
+
+ struct WorkForGroup
+ {
+ MIBlock *group;
+ QStack<MIBlock *> postponed;
+
+ WorkForGroup(MIBlock *group = nullptr) : group(group) {}
+ };
+ WorkForGroup currentGroup;
+ QStack<WorkForGroup> postponedGroups;
+ std::vector<MIBlock *> sequence;
+
+ class ProcessedBlocks
+ {
+ BitVector processed;
+
+ public:
+ ProcessedBlocks(MIFunction *function)
+ : processed(int(function->blockCount()), false)
+ {}
+
+ bool alreadyProcessed(MIBlock *bb) const
+ {
+ Q_ASSERT(bb);
+
+ return processed.at(bb->index());
+ }
+
+ void markAsProcessed(MIBlock *bb)
+ {
+ processed.setBit(bb->index());
+ }
+ } emitted;
+ QHash<MIBlock *, MIBlock *> loopsStartEnd;
+
+ bool checkCandidate(MIBlock *candidate);
+ MIBlock *pickNext();
+ void emitBlock(MIBlock *bb);
+ void schedule(MIBlock *functionEntryPoint);
+ void postpone(MIBlock *bb);
+ void dump() const;
+
+public:
+ BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo);
+
+ const std::vector<MIBlock *> &scheduledBlockSequence() const
+ { return sequence; }
+
+ QHash<MIBlock *, MIBlock *> loopEndsByStartBlock() const
+ { return loopsStartEnd; }
+};
+
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4BLOCKSCHEDULER_P_H
diff --git a/src/qml/jit/qv4domtree.cpp b/src/qml/jit/qv4domtree.cpp
new file mode 100644
index 0000000000..9484f4e2dc
--- /dev/null
+++ b/src/qml/jit/qv4domtree.cpp
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qbuffer.h>
+
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcDomTree, "qt.v4.ir.domTree")
+Q_LOGGING_CATEGORY(lcDomFrontier, "qt.v4.ir.domFrontier")
+
+DominatorTree::DominatorTree(MIFunction *f)
+ : m_function(f)
+ , m_data(new Data)
+{
+ calculateIDoms();
+ m_data.reset();
+}
+
+void DominatorTree::dumpImmediateDominators() const
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Immediate dominators for " << m_function->irFunction()->name() << ":" << endl;
+ for (MIBlock *to : m_function->blocks()) {
+ MIBlock::Index from = m_idom.at(to->index());
+ if (from != MIBlock::InvalidIndex)
+ qout << " " << from;
+ else
+ qout << " (none)";
+ qout << " dominates " << to->index() << endl;
+ }
+ qCDebug(lcDomTree, "%s", buf.data().constData());
+}
+
+void DominatorTree::setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator)
+{
+ if (m_idom.size() <= size_t(dominated))
+ m_idom.resize(dominated + 1);
+ m_idom[dominated] = dominator;
+}
+
+bool DominatorTree::dominates(MIBlock::Index dominator, MIBlock::Index dominated) const
+{
+ // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
+ Q_ASSERT(dominated != MIBlock::InvalidIndex);
+
+ if (dominator == dominated)
+ return false;
+
+ for (MIBlock::Index it = m_idom[dominated]; it != MIBlock::InvalidIndex; it = m_idom[it]) {
+ if (it == dominator)
+ return true;
+ }
+
+ return false;
+}
+
+// Calculate a depth-first iteration order on the nodes of the dominator tree.
+//
+// The order of the nodes in the vector is not the same as one where a recursive depth-first
+// iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
+// So for the:
+// 1 dominates 2
+// 2 dominates 3
+// 3 dominates 4
+// 2 dominates 5
+// the order will be:
+// 4, 3, 5, 2, 1
+// or:
+// 4, 5, 3, 2, 1
+// So the order of nodes on the same depth is undefined, but it will be after the nodes
+// they dominate, and before the nodes that dominate them.
+//
+// The reason for this order is that a proper DFS pre-/post-order would require inverting
+// the idom vector by either building a real tree datastructure or by searching the idoms
+// for siblings and children. Both have a higher time complexity than sorting by depth.
+std::vector<MIBlock *> DominatorTree::calculateDFNodeIterOrder() const
+{
+ std::vector<int> depths = calculateNodeDepths();
+ std::vector<MIBlock *> order = m_function->blocks();
+ std::sort(order.begin(), order.end(), [&depths](MIBlock *one, MIBlock *two) -> bool {
+ return depths.at(one->index()) > depths.at(two->index());
+ });
+ return order;
+}
+
+// Algorithm:
+// - for each node:
+// - get the depth of a node. If it's unknown (-1):
+// - get the depth of the immediate dominator.
+// - if that's unknown too, calculate it by calling calculateNodeDepth
+// - set the current node's depth to that of immediate dominator + 1
+std::vector<int> DominatorTree::calculateNodeDepths() const
+{
+ std::vector<int> nodeDepths(size_t(m_function->blockCount()), -1);
+ for (MIBlock *bb : m_function->blocks()) {
+ int &bbDepth = nodeDepths[bb->index()];
+ if (bbDepth == -1) {
+ const int immDom = m_idom[bb->index()];
+ if (immDom == -1) {
+ // no immediate dominator, so it's either the start block, or an unreachable block
+ bbDepth = 0;
+ } else {
+ int immDomDepth = nodeDepths[immDom];
+ if (immDomDepth == -1)
+ immDomDepth = calculateNodeDepth(immDom, nodeDepths);
+ bbDepth = immDomDepth + 1;
+ }
+ }
+ }
+ return nodeDepths;
+}
+
+// Algorithm:
+// - search for the first dominator of a node that has a known depth. As all nodes are
+// reachable from the start node, and that node's depth is 0, this is finite.
+// - while doing that search, put all unknown nodes in the worklist
+// - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
+// node's depth + 1
+// This way every node's depth is calculated once, and the complexity is O(n).
+int DominatorTree::calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const
+{
+ std::vector<int> worklist;
+ worklist.reserve(8);
+ int depth = -1;
+
+ do {
+ worklist.push_back(nodeIdx);
+ nodeIdx = m_idom[nodeIdx];
+ depth = nodeDepths[nodeIdx];
+ } while (depth == -1);
+
+ for (auto it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
+ nodeDepths[*it] = ++depth;
+
+ return depth;
+}
+
+namespace {
+struct DFSTodo {
+ MIBlock::Index node = MIBlock::InvalidIndex;
+ MIBlock::Index parent = MIBlock::InvalidIndex;
+
+ DFSTodo() = default;
+ DFSTodo(MIBlock::Index node, MIBlock::Index parent)
+ : node(node)
+ , parent(parent)
+ {}
+};
+} // anonymous namespace
+
+void DominatorTree::dfs(MIBlock::Index node)
+{
+ std::vector<DFSTodo> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+ DFSTodo todo(node, MIBlock::InvalidIndex);
+
+ while (true) {
+ MIBlock::Index n = todo.node;
+
+ if (m_data->dfnum[n] == 0) {
+ m_data->dfnum[n] = m_data->size;
+ m_data->vertex[m_data->size] = n;
+ m_data->parent[n] = todo.parent;
+ ++m_data->size;
+
+ MIBlock::OutEdges out = m_function->block(n)->outEdges();
+ for (int i = out.size() - 1; i > 0; --i)
+ worklist.emplace_back(out[i]->index(), n);
+
+ if (!out.isEmpty()) {
+ todo.node = out.first()->index();
+ todo.parent = n;
+ continue;
+ }
+ }
+
+ if (worklist.empty())
+ break;
+
+ todo = worklist.back();
+ worklist.pop_back();
+ }
+}
+
+void DominatorTree::link(MIBlock::Index p, MIBlock::Index n)
+{
+ m_data->ancestor[n] = p;
+ m_data->best[n] = n;
+}
+
+void DominatorTree::calculateIDoms()
+{
+ Q_ASSERT(m_function->block(0)->inEdges().count() == 0);
+
+ const size_t bbCount = m_function->blockCount();
+ m_data->vertex = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->parent = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->dfnum = std::vector<unsigned>(bbCount, 0);
+ m_data->semi = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->ancestor = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_idom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->samedom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+ m_data->best = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex);
+
+ QHash<MIBlock::Index, std::vector<MIBlock::Index>> bucket;
+ bucket.reserve(int(bbCount));
+
+ dfs(m_function->block(0)->index());
+
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(m_data->vertex.capacity() / 2);
+
+ for (int i = m_data->size - 1; i > 0; --i) {
+ MIBlock::Index n = m_data->vertex[i];
+ MIBlock::Index p = m_data->parent[n];
+ MIBlock::Index s = p;
+
+ for (auto inEdge : m_function->block(n)->inEdges()) {
+ if (inEdge->isDeoptBlock())
+ continue;
+ MIBlock::Index v = inEdge->index();
+ MIBlock::Index ss = MIBlock::InvalidIndex;
+ if (m_data->dfnum[v] <= m_data->dfnum[n])
+ ss = v;
+ else
+ ss = m_data->semi[ancestorWithLowestSemi(v, worklist)];
+ if (m_data->dfnum[ss] < m_data->dfnum[s])
+ s = ss;
+ }
+ m_data->semi[n] = s;
+ bucket[s].push_back(n);
+ link(p, n);
+ if (bucket.contains(p)) {
+ for (MIBlock::Index v : bucket[p]) {
+ MIBlock::Index y = ancestorWithLowestSemi(v, worklist);
+ MIBlock::Index semi_v = m_data->semi[v];
+ if (m_data->semi[y] == semi_v)
+ m_idom[v] = semi_v;
+ else
+ m_data->samedom[v] = y;
+ }
+ bucket.remove(p);
+ }
+ }
+
+ for (unsigned i = 1; i < m_data->size; ++i) {
+ MIBlock::Index n = m_data->vertex[i];
+ Q_ASSERT(n != MIBlock::InvalidIndex);
+ Q_ASSERT(!bucket.contains(n));
+ Q_ASSERT(m_data->ancestor[n] != MIBlock::InvalidIndex);
+ Q_ASSERT((m_data->semi[n] != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->ancestor[n]] <= m_data->dfnum[m_data->semi[n]])
+ || m_data->semi[n] == n);
+ MIBlock::Index sdn = m_data->samedom[n];
+ if (sdn != MIBlock::InvalidIndex)
+ m_idom[n] = m_idom[sdn];
+ }
+
+ if (lcDomTree().isDebugEnabled())
+ dumpImmediateDominators();
+
+ m_data.reset(nullptr);
+}
+
+MIBlock::Index DominatorTree::ancestorWithLowestSemi(MIBlock::Index v,
+ std::vector<MIBlock::Index> &worklist)
+{
+ worklist.clear();
+ for (MIBlock::Index it = v; it != MIBlock::InvalidIndex; it = m_data->ancestor[it])
+ worklist.push_back(it);
+
+ if (worklist.size() < 2)
+ return m_data->best[v];
+
+ MIBlock::Index b = MIBlock::InvalidIndex;
+ MIBlock::Index last = worklist.back();
+ Q_ASSERT(worklist.size() <= INT_MAX);
+ for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
+ MIBlock::Index bbIt = worklist[it];
+ m_data->ancestor[bbIt] = last;
+ MIBlock::Index &best_it = m_data->best[bbIt];
+ if (b != MIBlock::InvalidIndex
+ && m_data->dfnum[m_data->semi[b]] < m_data->dfnum[m_data->semi[best_it]]) {
+ best_it = b;
+ } else {
+ b = best_it;
+ }
+ }
+ return b;
+}
+
+void DominatorFrontier::compute(const DominatorTree &domTree)
+{
+ struct NodeProgress {
+ std::vector<MIBlock::Index> children;
+ std::vector<MIBlock::Index> todo;
+ };
+
+ MIFunction *function = domTree.function();
+ m_df.resize(function->blockCount());
+
+ // compute children of each node in the dominator tree
+ std::vector<std::vector<MIBlock::Index> > children; // BasicBlock index -> children
+ children.resize(function->blockCount());
+ for (MIBlock *n : function->blocks()) {
+ const MIBlock::Index nodeIndex = n->index();
+ Q_ASSERT(function->block(nodeIndex) == n);
+ const MIBlock::Index nodeDominator = domTree.immediateDominator(nodeIndex);
+ if (nodeDominator == MIBlock::InvalidIndex)
+ continue; // there is no dominator to add this node to as a child (e.g. the start node)
+ children[nodeDominator].push_back(nodeIndex);
+ }
+
+ // Fill the worklist and initialize the node status for each basic-block
+ std::vector<NodeProgress> nodeStatus;
+ nodeStatus.resize(function->blockCount());
+ std::vector<MIBlock::Index> worklist;
+ worklist.reserve(function->blockCount());
+ for (MIBlock *bb : function->blocks()) {
+ MIBlock::Index nodeIndex = bb->index();
+ worklist.push_back(nodeIndex);
+ NodeProgress &np = nodeStatus[nodeIndex];
+ np.children = children[nodeIndex];
+ np.todo = children[nodeIndex];
+ }
+
+ BitVector DF_done(int(function->blockCount()), false);
+
+ while (!worklist.empty()) {
+ MIBlock::Index node = worklist.back();
+
+ if (DF_done.at(node)) {
+ worklist.pop_back();
+ continue;
+ }
+
+ NodeProgress &np = nodeStatus[node];
+ auto it = np.todo.begin();
+ while (it != np.todo.end()) {
+ if (DF_done.at(*it)) {
+ it = np.todo.erase(it);
+ } else {
+ worklist.push_back(*it);
+ break;
+ }
+ }
+
+ if (np.todo.empty()) {
+ MIBlockSet &miBlockSet = m_df[node];
+ miBlockSet.init(function);
+ for (MIBlock *y : function->block(node)->outEdges()) {
+ if (domTree.immediateDominator(y->index()) != node)
+ miBlockSet.insert(y);
+ }
+ for (MIBlock::Index child : np.children) {
+ const MIBlockSet &ws = m_df[child];
+ for (auto w : ws) {
+ const MIBlock::Index wIndex = w->index();
+ if (node == wIndex || !domTree.dominates(node, w->index()))
+ miBlockSet.insert(w);
+ }
+ }
+ DF_done.setBit(node);
+ worklist.pop_back();
+ }
+ }
+
+ if (lcDomFrontier().isDebugEnabled())
+ dump(domTree.function());
+}
+
+void DominatorFrontier::dump(MIFunction *function)
+{
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ QTextStream qout(&buf);
+ qout << "Dominator Frontiers:" << endl;
+ for (MIBlock *n : function->blocks()) {
+ qout << "\tDF[" << n->index() << "]: {";
+ const MIBlockSet &SList = m_df[n->index()];
+ for (MIBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
+ if (i != SList.begin())
+ qout << ", ";
+ qout << (*i)->index();
+ }
+ qout << "}" << endl;
+ }
+ qCDebug(lcDomFrontier, "%s", buf.data().constData());
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4domtree_p.h b/src/qml/jit/qv4domtree_p.h
new file mode 100644
index 0000000000..703e17ab61
--- /dev/null
+++ b/src/qml/jit/qv4domtree_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4DOMTREE_P_H
+#define QV4DOMTREE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+#include "qv4miblockset_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree
+{
+ Q_DISABLE_COPY_MOVE(DominatorTree)
+
+public:
+ DominatorTree(MIFunction *f);
+ ~DominatorTree() = default;
+
+ void dumpImmediateDominators() const;
+ MIFunction *function() const
+ { return m_function; }
+
+ void setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator);
+
+ MIBlock::Index immediateDominator(MIBlock::Index blockIndex) const
+ { return m_idom[blockIndex]; }
+
+ bool dominates(MIBlock::Index dominator, MIBlock::Index dominated) const;
+
+ bool insideSameDominatorChain(MIBlock::Index one, MIBlock::Index other) const
+ { return one == other || dominates(one, other) || dominates(other, one); }
+
+ std::vector<MIBlock *> calculateDFNodeIterOrder() const;
+
+ std::vector<int> calculateNodeDepths() const;
+
+private: // functions
+ int calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const;
+ void link(MIBlock::Index p, MIBlock::Index n);
+ void calculateIDoms();
+ void dfs(MIBlock::Index node);
+ MIBlock::Index ancestorWithLowestSemi(MIBlock::Index v, std::vector<MIBlock::Index> &worklist);
+
+private: // data
+ struct Data {
+ std::vector<unsigned> dfnum; // MIBlock index -> dfnum
+ std::vector<MIBlock::Index> vertex;
+ std::vector<MIBlock::Index> parent; // MIBlock index -> parent MIBlock index
+ std::vector<MIBlock::Index> ancestor; // MIBlock index -> ancestor MIBlock index
+ std::vector<MIBlock::Index> best; // MIBlock index -> best MIBlock index
+ std::vector<MIBlock::Index> semi; // MIBlock index -> semi dominator MIBlock index
+ std::vector<MIBlock::Index> samedom; // MIBlock index -> same dominator MIBlock index
+ unsigned size = 0;
+ };
+
+ MIFunction *m_function;
+ QScopedPointer<Data> m_data;
+ std::vector<MIBlock::Index> m_idom; // MIBlock index -> immediate dominator MIBlock index
+};
+
+class DominatorFrontier
+{
+public:
+ DominatorFrontier(const DominatorTree &domTree)
+ { compute(domTree); }
+
+ const MIBlockSet &operator[](MIBlock *n) const
+ { return m_df[n->index()]; }
+
+private: // functions
+ void compute(const DominatorTree &domTree);
+ void dump(MIFunction *function);
+
+private: // data
+ std::vector<MIBlockSet> m_df; // MIBlock index -> dominator frontier
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4DOMTREE_P_H
diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp
index 94b8e86e08..2c073701ee 100644
--- a/src/qml/jit/qv4graphbuilder.cpp
+++ b/src/qml/jit/qv4graphbuilder.cpp
@@ -800,45 +800,10 @@ void GraphBuilder::generate_StoreSuperProperty(int property)
env()->accumulator());
}
-void GraphBuilder::generate_StoreScopeObjectProperty(int base, int propertyIndex)
+void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/)
{
- createNode(opBuilder()->get<Meta::QMLStoreScopeObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- env()->accumulator());
-}
-
-void GraphBuilder::generate_StoreContextObjectProperty(int base, int propertyIndex)
-{
- createNode(opBuilder()->get<Meta::QMLStoreContextObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- env()->accumulator());
-}
-
-void GraphBuilder::generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadScopeObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- createConstant(captureRequired)));
-}
-
-void GraphBuilder::generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadContextObjectProperty>(),
- env()->slot(base),
- createConstant(propertyIndex),
- createConstant(captureRequired)));
-}
-
-void GraphBuilder::generate_LoadIdObject(int index, int base)
-{
- bindAcc(createNode(opBuilder()->get<Meta::QMLLoadIdObject>(),
- env()->slot(base),
- createConstant(index)));
+ bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(),
+ createConstant(propertyIndex)));
}
void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); }
@@ -913,22 +878,12 @@ void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int
finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv);
}
-void GraphBuilder::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
- int /*traceSlot*/)
-{
- VarArgNodes args;
- args.append(env()->slot(base));
- args.append(createConstant(propIdx));
- finalizeCall(Meta::QMLCallScopeObjectProperty, args, argc, argv);
-}
-
-void GraphBuilder::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
- int /*traceSlot*/)
+void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
+ int /*traceSlot*/)
{
VarArgNodes args;
- args.append(env()->slot(base));
- args.append(createConstant(propIdx));
- finalizeCall(Meta::QMLCallContextObjectProperty, args, argc, argv);
+ args.append(createConstant(index));
+ finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv);
}
void GraphBuilder::generate_SetUnwindHandler(int offset)
@@ -1472,7 +1427,7 @@ void GraphBuilder::generate_UNot()
createToBoolean(env()->accumulator())));
}
-void GraphBuilder::generate_UPlus()
+void GraphBuilder::generate_UPlus(int /*traceSlot*/)
{
Node* control = createNode(opBuilder()->get<Meta::JSToNumber>(),
env()->accumulator());
@@ -1636,17 +1591,6 @@ void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/)
env()->accumulator()));
}
-void GraphBuilder::generate_LoadQmlContext(int result)
-{
- env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadContext>()), result);
-}
-
-void GraphBuilder::generate_LoadQmlImportedScripts(int result)
-{
- env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadImportedScripts>()),
- result);
-}
-
void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
for (int reg = firstReg; reg < firstReg + count; ++reg)
diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h
index 6393cab9ef..450d8640b7 100644
--- a/src/qml/jit/qv4graphbuilder_p.h
+++ b/src/qml/jit/qv4graphbuilder_p.h
@@ -173,15 +173,7 @@ protected: // ByteCodeHandler
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
- void generate_StoreScopeObjectProperty(int base,
- int propertyIndex) override;
- void generate_StoreContextObjectProperty(int base,
- int propertyIndex) override;
- void generate_LoadScopeObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadContextObjectProperty(int propertyIndex, int base,
- int captureRequired) override;
- void generate_LoadIdObject(int index, int base) override;
+ void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int offset) override;
@@ -196,10 +188,7 @@ protected: // ByteCodeHandler
void generate_CallName(int name, int argc, int argv, int traceSlot) override;
void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
- void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
- int traceSlot) override;
- void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
- int traceSlot) override;
+ void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
void generate_SetUnwindHandler(int offset) override;
void generate_UnwindDispatch() override;
void generate_UnwindToLabel(int level, int offset) override;
@@ -261,7 +250,7 @@ protected: // ByteCodeHandler
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
void generate_UNot() override;
- void generate_UPlus() override;
+ void generate_UPlus(int traceSlot) override;
void generate_UMinus(int traceSlot) override;
void generate_UCompl() override;
void generate_Increment(int traceSlot) override;
@@ -284,8 +273,6 @@ protected: // ByteCodeHandler
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
- void generate_LoadQmlContext(int result) override;
- void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;
diff --git a/src/qml/jit/qv4ir.cpp b/src/qml/jit/qv4ir.cpp
index 0b82330394..cb3eeeec60 100644
--- a/src/qml/jit/qv4ir.cpp
+++ b/src/qml/jit/qv4ir.cpp
@@ -78,7 +78,7 @@ QString Function::name() const
if (auto n = v4Function()->name())
name = n->toQString();
if (name.isEmpty())
- name.sprintf("%p", static_cast<void *>(v4Function()));
+ name = QString::asprintf("%p", v4Function());
auto loc = v4Function()->sourceLocation();
return name + QStringLiteral(" (%1:%2:%3)").arg(loc.sourceFile, QString::number(loc.line),
QString::number(loc.column));
@@ -159,7 +159,7 @@ QByteArray Dumper::dump(const Function *f)
name = n->toQString();
fo[QLatin1String("_searchKey")] = QStringLiteral("function %1").arg(name);
if (name.isEmpty())
- name.sprintf("%p", static_cast<void *>(f->v4Function()));
+ name = QString::asprintf("%p", f->v4Function());
fo[QLatin1String("name")] = name;
}
diff --git a/src/qml/jit/qv4loopinfo.cpp b/src/qml/jit/qv4loopinfo.cpp
new file mode 100644
index 0000000000..0366c49e30
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4loopinfo_p.h"
+#include "qv4domtree_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLoopinfo, "qt.v4.ir.loopinfo")
+
+void LoopInfo::detectLoops()
+{
+ blockInfos.resize(dt.function()->blockCount());
+
+ std::vector<MIBlock *> backedges;
+ backedges.reserve(4);
+
+ const auto order = dt.calculateDFNodeIterOrder();
+ for (MIBlock *bb : order) {
+ if (bb->isDeoptBlock())
+ continue;
+
+ backedges.clear();
+
+ for (MIBlock *pred : bb->inEdges()) {
+ if (bb == pred || dt.dominates(bb->index(), pred->index()))
+ backedges.push_back(pred);
+ }
+
+ if (!backedges.empty())
+ subLoop(bb, backedges);
+ }
+
+ collectLoopExits();
+
+ dump();
+}
+
+void LoopInfo::collectLoopExits()
+{
+ for (MIBlock::Index i = 0, ei = MIBlock::Index(blockInfos.size()); i != ei; ++i) {
+ BlockInfo &bi = blockInfos[i];
+ MIBlock *currentBlock = dt.function()->block(i);
+ if (bi.isLoopHeader) {
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != currentBlock && !inLoopOrSubLoop(outEdge, currentBlock))
+ bi.loopExits.push_back(outEdge);
+ }
+ }
+ if (MIBlock *containingLoop = bi.loopHeader) {
+ BlockInfo &loopInfo = blockInfos[containingLoop->index()];
+ for (MIBlock *outEdge : currentBlock->outEdges()) {
+ if (outEdge != containingLoop && !inLoopOrSubLoop(outEdge, containingLoop))
+ loopInfo.loopExits.push_back(outEdge);
+ }
+ }
+ }
+}
+
+bool LoopInfo::inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const
+{
+ const BlockInfo &bi = blockInfos[block->index()];
+ MIBlock *loopHeaderForBlock = bi.loopHeader;
+ if (loopHeaderForBlock == nullptr)
+ return false; // block is not in any loop
+
+ while (loopHeader) {
+ if (loopHeader == loopHeaderForBlock)
+ return true;
+ // look into the parent loop of loopHeader to see if block is contained there
+ loopHeader = blockInfos[loopHeader->index()].loopHeader;
+ }
+
+ return false;
+}
+
+void LoopInfo::subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges)
+{
+ blockInfos[loopHead->index()].isLoopHeader = true;
+
+ std::vector<MIBlock *> worklist;
+ worklist.reserve(backedges.size() + 8);
+ worklist.insert(worklist.end(), backedges.begin(), backedges.end());
+ while (!worklist.empty()) {
+ MIBlock *predIt = worklist.back();
+ worklist.pop_back();
+
+ MIBlock *subloop = blockInfos[predIt->index()].loopHeader;
+ if (subloop) {
+ // This is a discovered block. Find its outermost discovered loop.
+ while (MIBlock *parentLoop = blockInfos[subloop->index()].loopHeader)
+ subloop = parentLoop;
+
+ // If it is already discovered to be a subloop of this loop, continue.
+ if (subloop == loopHead)
+ continue;
+
+ // Yay, it's a subloop of this loop.
+ blockInfos[subloop->index()].loopHeader = loopHead;
+ predIt = subloop;
+
+ // Add all predecessors of the subloop header to the worklist, as long as
+ // those predecessors are not in the current subloop. It might be the case
+ // that they are in other loops, which we will then add as a subloop to the
+ // current loop.
+ for (MIBlock *predIn : predIt->inEdges())
+ if (blockInfos[predIn->index()].loopHeader != subloop)
+ worklist.push_back(predIn);
+ } else {
+ if (predIt == loopHead)
+ continue;
+
+ // This is an undiscovered block. Map it to the current loop.
+ blockInfos[predIt->index()].loopHeader = loopHead;
+
+ // Add all incoming edges to the worklist.
+ for (MIBlock *bb : predIt->inEdges())
+ worklist.push_back(bb);
+ }
+ }
+}
+
+void LoopInfo::dump() const
+{
+ if (!lcLoopinfo().isDebugEnabled())
+ return;
+
+ QString s = QStringLiteral("Loop information:\n");
+ for (size_t i = 0, ei = blockInfos.size(); i != ei; ++i) {
+ const BlockInfo &bi = blockInfos[i];
+ s += QStringLiteral(" %1 : is loop header: %2, contained in loop header's loop: ")
+ .arg(i).arg(bi.isLoopHeader ? QLatin1String("yes") : QLatin1String("no"));
+ if (bi.loopHeader)
+ s += QString::number(bi.loopHeader->index());
+ else
+ s += QLatin1String("<none>");
+ if (bi.isLoopHeader) {
+ s += QStringLiteral(", loop exits: ");
+ if (bi.loopExits.empty()) {
+ s += QLatin1String("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *exit : bi.loopExits) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QString::number(exit->index());
+ }
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ qCDebug(lcLoopinfo).noquote().nospace() << s;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4loopinfo_p.h b/src/qml/jit/qv4loopinfo_p.h
new file mode 100644
index 0000000000..6a865e6dc6
--- /dev/null
+++ b/src/qml/jit/qv4loopinfo_p.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4LOOPINFO_P_H
+#define QV4LOOPINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+
+#include <QHash>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class DominatorTree;
+
+// Detect all (sub-)loops in a function.
+//
+// Doing loop detection on the CFG is better than relying on the statement information in
+// order to mark loops. Although JavaScript only has natural loops, it can still be the case
+// that something is not a loop even though a loop-like-statement is in the source. For
+// example:
+// while (true) {
+// if (i > 0)
+// break;
+// else
+// break;
+// }
+//
+// Algorithm:
+// - do a DFS on the dominator tree, where for each node:
+// - collect all back-edges
+// - if there are back-edges, the node is a loop-header for a new loop, so:
+// - walk the CFG is reverse-direction, and for every node:
+// - if the node already belongs to a loop, we've found a nested loop:
+// - get the loop-header for the (outermost) nested loop
+// - add that loop-header to the current loop
+// - continue by walking all incoming edges that do not yet belong to the current loop
+// - if the node does not belong to a loop yet, add it to the current loop, and
+// go on with all incoming edges
+//
+// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
+// an incoming edge where the other node is dominated by the current node. Meaning: all
+// execution paths that reach that other node have to go through the current node, that other
+// node ends with a (conditional) jump back to the loop header.
+//
+// The exact order of the DFS on the dominator tree is not important. The only property has to
+// be that a node is only visited when all the nodes it dominates have been visited before.
+// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
+// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
+// their containing loops, which makes nested-loop identification free. An added benefit is
+// that the nodes for those sub-loops are only processed once.
+//
+// Note: independent loops that share the same header are merged together. For example, in
+// the code snippet below, there are 2 back-edges into the loop-header, but only one single
+// loop will be detected.
+// while (a) {
+// if (b)
+// continue;
+// else
+// continue;
+// }
+class LoopInfo
+{
+ Q_DISABLE_COPY_MOVE(LoopInfo)
+
+ struct BlockInfo
+ {
+ MIBlock *loopHeader = nullptr;
+ bool isLoopHeader = false;
+ std::vector<MIBlock *> loopExits;
+ };
+
+public:
+ LoopInfo(const DominatorTree &dt)
+ : dt(dt)
+ {}
+
+ ~LoopInfo() = default;
+
+ void detectLoops();
+
+ MIBlock *loopHeaderFor(MIBlock *bodyBlock) const
+ { return blockInfos[bodyBlock->index()].loopHeader; }
+
+ bool isLoopHeader(MIBlock *block) const
+ { return blockInfos[block->index()].isLoopHeader; }
+
+ const std::vector<MIBlock *> loopExitsForLoop(MIBlock *loopHeader) const
+ { return blockInfos[loopHeader->index()].loopExits; }
+
+private:
+ void subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges);
+ void collectLoopExits();
+ bool inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const;
+
+ void dump() const;
+
+private:
+ const DominatorTree &dt;
+ std::vector<BlockInfo> blockInfos;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4LOOPINFO_P_H
diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp
new file mode 100644
index 0000000000..3b3711e7fa
--- /dev/null
+++ b/src/qml/jit/qv4lowering.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QLoggingCategory>
+
+#include "qv4lowering_p.h"
+#include "qv4graph_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering")
+
+GenericLowering::GenericLowering(Function &f)
+ : m_function(f)
+{}
+
+void GenericLowering::lower()
+{
+ NodeWorkList worklist(graph());
+ // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and
+ // have any clean-up done afterwards.
+ worklist.enqueueAllInputs(graph()->endNode());
+
+ while (Node *n = worklist.dequeueNextNodeForVisiting()) {
+ worklist.enqueueAllInputs(n);
+
+ if (!CallPayload::isRuntimeCall(n->opcode()))
+ continue;
+
+ if (CallPayload::isVarArgsCall(n->opcode()))
+ replaceWithVarArgsCall(n);
+ else
+ replaceWithCall(n);
+ }
+}
+
+void GenericLowering::replaceWithCall(Node *n)
+{
+ auto newOp = opBuilder()->getCall(n->opcode());
+
+ QVarLengthArray<Node *, 32> args;
+ if (CallPayload::takesEngineAsArg(n->opcode(), 0))
+ args.append(graph()->engineNode());
+ if (CallPayload::takesFunctionAsArg(n->opcode(), args.size()))
+ args.append(graph()->functionNode());
+ if (CallPayload::takesFrameAsArg(n->opcode(), args.size()))
+ args.append(graph()->cppFrameNode());
+ const int extraLeadingArguments = args.size();
+
+ for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) {
+ Node *input = n->input(arg);
+ if (input->opcode() == Meta::FrameState)
+ continue;
+
+ if (arg >= n->operation()->valueInputCount()) {
+ // effect or control input
+ args.append(input);
+ continue;
+ }
+
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+
+ args.append(input);
+ }
+
+ Node *newCall = graph()->createNode(newOp, args.data(), args.size());
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode()));
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+
+ for (Node *use : newCall->uses()) {
+ // fix-up indices for SelectOutput:
+ if (use->opcode() == Meta::SelectOutput) {
+ const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32();
+ const int newIndex = oldIndex + extraLeadingArguments;
+ use->replaceInput(1, graph()->createConstantIntNode(newIndex));
+ use->replaceInput(2, newCall->input(newIndex));
+ break;
+ }
+ }
+}
+
+void GenericLowering::replaceWithVarArgsCall(Node *n)
+{
+ const bool isTailCall = n->opcode() == Meta::JSTailCall;
+ Operation *newOp = isTailCall ? opBuilder()->getTailCall()
+ : opBuilder()->getCall(n->opcode());
+
+ //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases
+
+ const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering.
+ Node *vaAlloc = graph()->createNode(
+ opBuilder()->get<Meta::VAAlloc>(),
+ graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart),
+ n->effectInput());
+ QVarLengthArray<Node *, 32> vaSealIn;
+ vaSealIn.append(vaAlloc);
+ for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) {
+ vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc,
+ graph()->createConstantIntNode(vaSealIn.size() - 1),
+ n->input(i)));
+ }
+ vaSealIn.append(vaAlloc);
+ Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2),
+ vaSealIn.data(),
+ vaSealIn.size());
+ QVarLengthArray<Node *, 8> callArgs;
+ if (isTailCall)
+ callArgs.append(graph()->cppFrameNode());
+ callArgs.append(graph()->engineNode());
+ for (unsigned i = 0; i != varArgsStart; ++i) {
+ Node *input = n->input(i);
+ if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(),
+ function().nodeInfo(input)->type()))
+ input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input);
+ callArgs.append(input);
+ }
+ callArgs.append(vaSeal); // args
+ if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck
+ callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc
+ callArgs.append(vaSeal); // effect
+ callArgs.append(n->controlInput(0)); // control flow
+ Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size()));
+
+ qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString()
+ << "with node" << newCall->id() << newOp->debugString();
+ qCDebug(lcLower) << "... old node #inputs:" << n->inputCount();
+ qCDebug(lcLower) << "... old node #uses:" << n->useCount();
+
+ n->replaceAllUsesWith(newCall);
+ n->kill();
+
+ qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount();
+ qCDebug(lcLower) << "... new node #uses:" << newCall->useCount();
+}
+
+bool GenericLowering::allUsesAsUnboxedBool(Node *n)
+{
+ for (Node *use : n->uses()) {
+ if (use->operation()->kind() != Meta::Branch)
+ return false;
+ }
+
+ return true;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4lowering_p.h b/src/qml/jit/qv4lowering_p.h
new file mode 100644
index 0000000000..0b482bc9f0
--- /dev/null
+++ b/src/qml/jit/qv4lowering_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4LOWERING_P_H
+#define QV4LOWERING_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qqmljsmemorypool_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4util_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4graph_p.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32
+// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This
+// transforms the graph into something that is closer to actual executable code.
+
+
+// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing
+// smart here, all that should have been done before this phase.
+class GenericLowering final
+{
+ Q_DISABLE_COPY(GenericLowering)
+
+public:
+ GenericLowering(Function &f);
+
+ void lower();
+
+private:
+ void replaceWithCall(Node *n);
+ void replaceWithVarArgsCall(Node *n);
+ static bool allUsesAsUnboxedBool(Node *n);
+
+ Function &function()
+ { return m_function; }
+
+ Graph *graph()
+ { return function().graph(); }
+
+ OperationBuilder *opBuilder()
+ { return graph()->opBuilder(); }
+
+private:
+ Function &m_function;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4LOWERING_P_H
diff --git a/src/qml/jit/qv4mi.cpp b/src/qml/jit/qv4mi.cpp
new file mode 100644
index 0000000000..f0b172243d
--- /dev/null
+++ b/src/qml/jit/qv4mi.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+#include <private/qqmlglobal_p.h>
+
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcMI, "qt.v4.ir.mi")
+
+QString MIOperand::debugString() const
+{
+ switch (kind()) {
+ case Invalid: return QStringLiteral("<<INVALID>>");
+ case Constant: return ConstantPayload::debugString(constantValue());
+ case VirtualRegister: return QStringLiteral("vreg%1").arg(virtualRegister());
+ case EngineRegister: return QStringLiteral("engine");
+ case CppFrameRegister: return QStringLiteral("cppFrame");
+ case Function: return QStringLiteral("function");
+ case JSStackSlot: return QStringLiteral("jsstack[%1]").arg(stackSlot());
+ case BoolStackSlot: return QStringLiteral("bstack[%1]").arg(stackSlot());
+ case JumpTarget: return targetBlock() ? QStringLiteral("L%1").arg(targetBlock()->index())
+ : QStringLiteral("<<INVALID BLOCK>>");
+ default: Q_UNREACHABLE();
+ }
+}
+
+MIInstr *MIInstr::create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands)
+{
+ return pool->New<MIInstr>(irNode, pool, nOperands);
+}
+
+static QString commentIndent(const QString &line)
+{
+ int spacing = std::max(70 - line.length(), 1);
+ return line + QString(spacing, QLatin1Char(' '));
+}
+
+static QString indent(int nr)
+{
+ QString s = nr == -1 ? QString() : QString::number(nr);
+ int padding = 6 - s.size();
+ if (padding > 0)
+ s = QString(padding, QLatin1Char(' ')) + s;
+ return s;
+}
+
+MIFunction::MIFunction(Function *irFunction)
+ : m_irFunction(irFunction)
+{}
+
+void MIFunction::renumberBlocks()
+{
+ for (size_t i = 0, ei = m_blocks.size(); i != ei; ++i) {
+ MIBlock *b = m_blocks[i];
+ b->setIndex(unsigned(i));
+ }
+}
+
+void MIFunction::renumberInstructions()
+{
+ int pos = 0;
+ for (MIBlock *b : m_blocks) {
+ for (MIInstr &instr : b->instructions()) {
+ pos += 2;
+ instr.setPosition(pos);
+ }
+ }
+}
+
+void MIFunction::dump(const QString &description) const
+{
+ if (!lcMI().isDebugEnabled())
+ return;
+
+ QString s = description + QLatin1String(":\n");
+ QString name;
+ if (auto n = irFunction()->v4Function()->name())
+ name = n->toQString();
+ if (name.isEmpty())
+ QString::asprintf("%p", static_cast<void *>(irFunction()->v4Function()));
+ QString line = QStringLiteral("function %1 {").arg(name);
+ auto loc = irFunction()->v4Function()->sourceLocation();
+ s += commentIndent(line) + QStringLiteral("; %1:%2:%3\n").arg(loc.sourceFile,
+ QString::number(loc.line),
+ QString::number(loc.column));
+ for (const MIBlock *b : blocks()) {
+ line = QStringLiteral("L%1").arg(b->index());
+ bool first = true;
+ if (!b->arguments().empty()) {
+ line += QLatin1Char('(');
+ for (const MIOperand &arg : b->arguments()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += arg.debugString();
+ }
+ line += QLatin1Char(')');
+ }
+ line += QLatin1Char(':');
+ line = commentIndent(line) + QStringLiteral("; preds: ");
+ if (b->inEdges().isEmpty()) {
+ line += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *in : b->inEdges()) {
+ if (first)
+ first = false;
+ else
+ line += QStringLiteral(", ");
+ line += QStringLiteral("L%1").arg(in->index());
+ }
+ }
+ s += line + QLatin1Char('\n');
+ for (const MIInstr &i : b->instructions()) {
+ line = indent(i.position()) + QLatin1String(": ");
+ if (i.hasDestination())
+ line += i.destination().debugString() + QStringLiteral(" = ");
+ line += i.irNode()->operation()->debugString();
+ bool first = true;
+ for (const MIOperand &op : i.operands()) {
+ if (first)
+ first = false;
+ else
+ line += QLatin1Char(',');
+ line += QLatin1Char(' ') + op.debugString();
+ }
+ line = commentIndent(line) + QStringLiteral("; node-id: %1").arg(i.irNode()->id());
+ if (i.irNode()->operation()->needsBytecodeOffsets())
+ line += QStringLiteral(", bytecode-offset: %1").arg(irFunction()->nodeInfo(i.irNode())->currentInstructionOffset());
+ s += line + QLatin1Char('\n');
+ }
+ s += commentIndent(QString()) + QStringLiteral("; succs: ");
+ if (b->outEdges().isEmpty()) {
+ s += QStringLiteral("<none>");
+ } else {
+ bool first = true;
+ for (MIBlock *succ : b->outEdges()) {
+ if (first)
+ first = false;
+ else
+ s += QStringLiteral(", ");
+ s += QStringLiteral("L%1").arg(succ->index());
+ }
+ }
+ s += QLatin1Char('\n');
+ }
+ s += QLatin1Char('}');
+
+ for (const QStringRef &line : s.splitRef('\n'))
+ qCDebug(lcMI).noquote().nospace() << line;
+}
+
+unsigned MIFunction::extraJSSlots() const
+{
+ uint interpreterFrameSize = CppStackFrame::requiredJSStackFrameSize(irFunction()->v4Function());
+ if (m_jsSlotCount <= interpreterFrameSize)
+ return 0;
+ return m_jsSlotCount - interpreterFrameSize;
+}
+
+void MIFunction::setStartBlock(MIBlock *newStartBlock)
+{
+ auto it = std::find(m_blocks.begin(), m_blocks.end(), newStartBlock);
+ Q_ASSERT(it != m_blocks.end());
+ std::swap(*m_blocks.begin(), *it);
+}
+
+void MIFunction::setStackSlotCounts(unsigned dword, unsigned qword, unsigned js)
+{
+ m_vregCount = 0;
+ m_dwordSlotCount = dword;
+ m_qwordSlotCount = qword;
+ m_jsSlotCount = js;
+}
+
+void MIFunction::verifyCFG() const
+{
+ if (block(MIFunction::StartBlockIndex)->instructions().front().opcode() != Meta::Start)
+ qFatal("MIFunction block 0 is not the start block");
+
+ for (MIBlock *b : m_blocks) {
+ for (MIBlock *in : b->inEdges()) {
+ if (!in->outEdges().contains(b))
+ qFatal("block %u has incoming edge from block %u, "
+ "but does not appear in that block's outgoing edges",
+ b->index(), in->index());
+ }
+ for (MIBlock *out : b->outEdges()) {
+ if (!out->inEdges().contains(b))
+ qFatal("block %u has outgoing edge from block %u, "
+ "but does not appear in that block's incoming edges",
+ b->index(), out->index());
+ }
+ }
+}
+
+MIBlock *MIBlock::findEdgeTo(Operation::Kind target) const
+{
+ for (MIBlock *outEdge : outEdges()) {
+ if (outEdge->instructions().front().opcode() == target)
+ return outEdge;
+ }
+ return nullptr;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h
new file mode 100644
index 0000000000..f976d1dc94
--- /dev/null
+++ b/src/qml/jit/qv4mi_p.h
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MI_P_H
+#define QV4MI_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4ir_p.h>
+#include <private/qv4node_p.h>
+#include <private/qv4operation_p.h>
+
+#include <llvm/ADT/iterator.h>
+#include <llvm/ADT/iterator_range.h>
+#include <llvm/ADT/ilist.h>
+#include <llvm/ADT/ilist_node.h>
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler
+// will operate:
+
+class MIFunction; // containing all basic blocks, and a reference to the IR function
+
+class MIBlock; // containing an ordered sequence of instructions
+
+class MIInstr; // containing operands, and a reference to the IR node, that indicates which
+ // operation is represented by an instruction
+
+class MIOperand; // contains a description of where to get/put the input/result of an operation
+
+// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on
+// the native stack is divided in two parts: the quad-word part and the double-word part. The
+// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part
+// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know
+// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it
+// correctly.
+class MIOperand final
+{
+public:
+ enum Kind {
+ Invalid = 0,
+ Constant,
+ VirtualRegister,
+
+ EngineRegister,
+ CppFrameRegister,
+ Function,
+
+ JSStackSlot,
+ BoolStackSlot,
+
+ JumpTarget,
+ };
+
+ using List = QQmlJS::FixedPoolArray<MIOperand>;
+
+public:
+ MIOperand() = default;
+
+ static MIOperand createConstant(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Constant;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createVirtualRegister(Node *irNode, unsigned vreg)
+ {
+ MIOperand op;
+ op.m_kind = VirtualRegister;
+ op.m_irNode = irNode;
+ op.m_vreg = vreg;
+ return op;
+ }
+
+ static MIOperand createEngineRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = EngineRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createCppFrameRegister(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = CppFrameRegister;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createFunction(Node *irNode)
+ {
+ MIOperand op;
+ op.m_kind = Function;
+ op.m_irNode = irNode;
+ return op;
+ }
+
+ static MIOperand createJSStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = JSStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ static MIOperand createBoolStackSlot(Node *irNode, unsigned slot)
+ {
+ MIOperand op;
+ op.m_kind = BoolStackSlot;
+ op.m_irNode = irNode;
+ op.m_slot = slot;
+ return op;
+ }
+
+ //### or name this createDeoptBlock?
+ static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock)
+ {
+ MIOperand op;
+ op.m_kind = JumpTarget;
+ op.m_irNode = irNode;
+ op.m_targetBlock = targetBlock;
+ return op;
+ }
+
+ Kind kind() const
+ { return m_kind; }
+
+ bool isValid() const
+ { return m_kind != Invalid; }
+
+ bool isConstant() const
+ { return m_kind == Constant; }
+
+ bool isVirtualRegister() const
+ { return kind() == VirtualRegister; }
+
+ bool isEngineRegister() const
+ { return kind() == EngineRegister; }
+
+ bool isCppFrameRegister() const
+ { return kind() == CppFrameRegister; }
+
+ bool isFunction() const
+ { return kind() == Function; }
+
+ bool isJSStackSlot() const
+ { return kind() == JSStackSlot; }
+
+ bool isBoolStackSlot() const
+ { return kind() == BoolStackSlot; }
+
+ bool isStackSlot() const
+ { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); }
+
+ bool isJumpTarget() const
+ { return kind() == JumpTarget; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ inline Type nodeType(MIFunction *f) const;
+
+ QString debugString() const;
+
+ QV4::Value constantValue() const
+ {
+ Q_ASSERT(isConstant());
+ if (irNode()->opcode() == Meta::Undefined)
+ return QV4::Value::undefinedValue();
+ if (irNode()->opcode() == Meta::Empty)
+ return QV4::Value::emptyValue();
+ return ConstantPayload::get(*irNode()->operation())->value();
+ }
+
+ unsigned virtualRegister() const
+ { Q_ASSERT(isVirtualRegister()); return m_vreg; }
+
+ unsigned stackSlot() const
+ { Q_ASSERT(isStackSlot()); return m_slot; }
+
+ MIBlock *targetBlock() const
+ { Q_ASSERT(isJumpTarget()); return m_targetBlock; }
+
+ inline bool operator==(const MIOperand &other) const
+ {
+ if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ switch (kind()) {
+ case MIOperand::Invalid:
+ return !other.isValid();
+ case MIOperand::Constant:
+ return constantValue().asReturnedValue() == other.constantValue().asReturnedValue();
+ case MIOperand::VirtualRegister:
+ return virtualRegister() == other.virtualRegister();
+ case MIOperand::JumpTarget:
+ return targetBlock() == other.targetBlock();
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+ }
+
+ bool isDWordSlot() const
+ {
+ switch (kind()) {
+ case BoolStackSlot:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isQWordSlot() const
+ {
+ switch (kind()) {
+ //### TODO: double slots
+ default:
+ return false;
+ }
+ }
+
+ bool overlaps(const MIOperand &other) const
+ {
+ if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot()))
+ ; // fine, these are the same
+ else if (kind() != other.kind())
+ return false;
+
+ if (isStackSlot())
+ return stackSlot() == other.stackSlot();
+
+ return false;
+ }
+
+private:
+ Node *m_irNode = nullptr;
+ union {
+ unsigned m_vreg;
+ unsigned m_slot;
+ MIBlock *m_targetBlock = nullptr;
+ };
+ Kind m_kind = Invalid;
+};
+
+template <typename NodeTy> struct MIInstrListParentType {};
+template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; };
+
+template <typename NodeTy> class MIInstrList;
+
+template <typename MISubClass>
+class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass>
+{
+protected:
+ using ListTy = MIInstrList<MISubClass>;
+ using iterator = typename llvm::simple_ilist<MISubClass>::iterator;
+ using ItemParentClass = typename MIInstrListParentType<MISubClass>::type;
+
+public:
+ MIInstrListTraits() = default;
+
+protected:
+ void setListOwner(ItemParentClass *listOwner)
+ { m_owner = listOwner; }
+
+private:
+ ItemParentClass *m_owner = nullptr;
+
+ /// getListOwner - Return the object that owns this list. If this is a list
+ /// of instructions, it returns the BasicBlock that owns them.
+ ItemParentClass *getListOwner() const {
+ return m_owner;
+ }
+
+ static ListTy &getList(ItemParentClass *Par) {
+ return Par->*(Par->getSublistAccess());
+ }
+
+ static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) {
+ return Par ? toPtr(Par->getValueSymbolTable()) : nullptr;
+ }
+
+public:
+ void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); }
+ void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); }
+ void transferNodesFromList(MIInstrListTraits &L2, iterator first,
+ iterator last);
+};
+
+template <class T>
+class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>>
+{
+public:
+ MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner)
+ { this->setListOwner(owner); }
+};
+
+class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock>
+{
+ Q_DISABLE_COPY_MOVE(MIInstr) // heap use only!
+
+protected:
+ friend class QQmlJS::MemoryPool;
+ MIInstr() : m_operands(nullptr, 0) {}
+
+ explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands)
+ : m_irNode(irNode)
+ , m_operands(pool, nOperands)
+ {}
+
+ ~MIInstr() = default;
+
+public:
+ static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands);
+
+ MIBlock *parent() const
+ { return m_parent; }
+
+ MIBlock *getParent() const // for ilist_node_with_parent
+ { return parent(); }
+
+ void setParent(MIBlock *parent)
+ { m_parent = parent; }
+
+ Node *irNode() const
+ { return m_irNode; }
+
+ Operation::Kind opcode() const
+ { return m_irNode->opcode(); }
+
+ int position() const
+ { return m_position; }
+
+ inline void insertBefore(MIInstr *insertPos);
+ inline void insertAfter(MIInstr *insertPos);
+ inline MIInstrList<MIInstr>::iterator eraseFromParent();
+
+ bool hasDestination() const
+ { return m_destination.isValid(); }
+
+ MIOperand destination() const
+ { return m_destination; }
+
+ void setDestination(const MIOperand &dest)
+ { m_destination = dest; }
+
+ const MIOperand &operand(unsigned index) const
+ { return m_operands.at(index); }
+
+ void setOperand(unsigned index, const MIOperand &op)
+ { m_operands.at(index) = op; }
+
+ MIOperand &operand(unsigned index)
+ { return m_operands.at(index); }
+
+ const MIOperand::List &operands() const
+ { return m_operands; }
+
+ MIOperand::List &operands()
+ { return m_operands; }
+
+private:
+ friend MIFunction;
+ void setPosition(int newPosition)
+ { m_position = newPosition; }
+
+private:
+ MIBlock *m_parent = nullptr;
+ Node *m_irNode = nullptr;
+ int m_position = -1;
+ MIOperand m_destination;
+ MIOperand::List m_operands;
+};
+
+class MIBlock final
+{
+ Q_DISABLE_COPY_MOVE(MIBlock)
+
+public:
+ using Index = unsigned;
+ enum : Index { InvalidIndex = std::numeric_limits<Index>::max() };
+
+ using MIInstructionList = MIInstrList<MIInstr>;
+
+ using InEdges = QVarLengthArray<MIBlock *, 4>;
+ using OutEdges = QVarLengthArray<MIBlock *, 2>;
+
+protected:
+ friend MIFunction;
+ explicit MIBlock(Index index)
+ : m_instructions(this),
+ m_index(index)
+ {}
+
+ void setIndex(Index newIndex)
+ { m_index = newIndex; }
+
+public:
+ ~MIBlock() = default;
+
+ const MIInstructionList &instructions() const
+ { return m_instructions; }
+
+ MIInstructionList &instructions()
+ { return m_instructions; }
+
+ static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr)
+ { return &MIBlock::m_instructions; }
+
+ void addArgument(MIOperand &&arg)
+ { m_arguments.push_back(arg); }
+
+ const std::vector<MIOperand> &arguments() const
+ { return m_arguments; }
+
+ std::vector<MIOperand> &arguments()
+ { return m_arguments; }
+
+ void clearArguments()
+ { m_arguments.resize(0); }
+
+ const InEdges &inEdges() const
+ { return m_inEdges; }
+
+ void addInEdge(MIBlock *edge)
+ { m_inEdges.append(edge); }
+
+ const OutEdges &outEdges() const
+ { return m_outEdges; }
+
+ void addOutEdge(MIBlock *edge)
+ { m_outEdges.append(edge); }
+
+ Index index() const
+ { return m_index; }
+
+ MIBlock *findEdgeTo(Operation::Kind target) const;
+
+ bool isDeoptBlock() const
+ { return m_isDeoptBlock; }
+
+ void markAsDeoptBlock()
+ { m_isDeoptBlock = true; }
+
+private:
+ std::vector<MIOperand> m_arguments;
+ MIInstructionList m_instructions;
+ InEdges m_inEdges;
+ OutEdges m_outEdges;
+ Index m_index;
+ bool m_isDeoptBlock = false;
+};
+
+class MIFunction final
+{
+ Q_DISABLE_COPY_MOVE(MIFunction)
+
+public:
+ static constexpr MIBlock::Index StartBlockIndex = 0;
+
+public:
+ MIFunction(Function *irFunction);
+ ~MIFunction()
+ { qDeleteAll(m_blocks); }
+
+ Function *irFunction() const
+ { return m_irFunction; }
+
+ void setStartBlock(MIBlock *newStartBlock);
+ void renumberBlocks();
+ void renumberInstructions();
+
+ void dump(const QString &description) const;
+
+ size_t blockCount() const
+ { return blocks().size(); }
+
+ MIBlock *block(MIBlock::Index index) const
+ { return m_blocks[index]; }
+
+ const std::vector<MIBlock *> &blocks() const
+ { return m_blocks; }
+
+ MIBlock *addBlock()
+ {
+ auto *b = new MIBlock(unsigned(m_blocks.size()));
+ m_blocks.push_back(b);
+ return b;
+ }
+
+ void setBlockOrder(const std::vector<MIBlock *> &newSequence)
+ { m_blocks = newSequence; }
+
+ unsigned vregCount() const
+ { return m_vregCount; }
+
+ void setVregCount(unsigned vregCount)
+ { m_vregCount = vregCount; }
+
+ unsigned dwordSlotCount() const
+ { return m_dwordSlotCount; }
+
+ unsigned qwordSlotCount() const
+ { return m_qwordSlotCount; }
+
+ unsigned jsSlotCount() const
+ { return m_jsSlotCount; }
+
+ unsigned extraJSSlots() const;
+
+ void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js);
+
+ void verifyCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ std::vector<MIBlock *> m_blocks;
+ unsigned m_vregCount = 0;
+ unsigned m_dwordSlotCount = 0;
+ unsigned m_qwordSlotCount = 0;
+ unsigned m_jsSlotCount = 0;
+};
+
+Type MIOperand::nodeType(MIFunction *f) const
+{
+ return f->irFunction()->nodeInfo(irNode())->type();
+}
+
+inline uint qHash(const MIOperand &key, uint seed)
+{
+ uint h = ::qHash(key.kind(), seed);
+ switch (key.kind()) {
+ case MIOperand::VirtualRegister:
+ h ^= key.virtualRegister();
+ break;
+ case MIOperand::BoolStackSlot: Q_FALLTHROUGH();
+ case MIOperand::JSStackSlot:
+ h ^= key.stackSlot();
+ break;
+ default:
+ qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData());
+ }
+ return h;
+}
+
+void MIInstr::insertBefore(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(insertPos->getIterator(), this);
+}
+
+void MIInstr::insertAfter(MIInstr *insertPos)
+{
+ insertPos->parent()->instructions().insert(++insertPos->getIterator(), this);
+}
+
+MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent()
+{
+ auto p = parent();
+ setParent(nullptr);
+ return p->instructions().erase(getIterator());
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MI_P_H
diff --git a/src/qml/jit/qv4miblockset_p.h b/src/qml/jit/qv4miblockset_p.h
new file mode 100644
index 0000000000..5f814b99e0
--- /dev/null
+++ b/src/qml/jit/qv4miblockset_p.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MIBLOCKSET_P_H
+#define QV4MIBLOCKSET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4mi_p.h"
+#include "qv4util_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+class MIBlockSet
+{
+ using Flags = BitVector;
+
+ QVarLengthArray<MIBlock::Index, 8> blockNumbers;
+ Flags *blockFlags = nullptr;
+ MIFunction *function = nullptr;
+ enum { MaxVectorCapacity = 8 };
+
+public:
+ class const_iterator;
+ friend class const_iterator;
+
+public:
+ MIBlockSet(MIFunction *f = nullptr)
+ {
+ if (f)
+ init(f);
+ }
+
+ MIBlockSet(MIBlockSet &&other) noexcept
+ {
+ std::swap(blockNumbers, other.blockNumbers);
+ std::swap(blockFlags, other.blockFlags);
+ std::swap(function, other.function);
+ }
+
+ MIBlockSet(const MIBlockSet &other)
+ : function(other.function)
+ {
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ }
+
+ MIBlockSet &operator=(const MIBlockSet &other)
+ {
+ if (blockFlags) {
+ delete blockFlags;
+ blockFlags = nullptr;
+ }
+ function = other.function;
+ if (other.blockFlags)
+ blockFlags = new Flags(*other.blockFlags);
+ blockNumbers = other.blockNumbers;
+ return *this;
+ }
+
+ MIBlockSet &operator=(MIBlockSet &&other) noexcept
+ {
+ if (&other != this) {
+ std::swap(blockNumbers, other.blockNumbers);
+
+ delete blockFlags;
+ blockFlags = other.blockFlags;
+ other.blockFlags = nullptr;
+
+ function = other.function;
+ }
+ return *this;
+ }
+
+ ~MIBlockSet()
+ {
+ delete blockFlags;
+ }
+
+ void init(MIFunction *f)
+ {
+ Q_ASSERT(!function);
+ Q_ASSERT(f);
+ function = f;
+ }
+
+ bool empty() const;
+
+ void insert(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->setBit(bb->index());
+ return;
+ }
+
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ if (blockNumber == bb->index())
+ return;
+ }
+
+ if (blockNumbers.size() == MaxVectorCapacity) {
+ blockFlags = new Flags(int(function->blockCount()), false);
+ for (unsigned int blockNumber : qAsConst(blockNumbers)) {
+ blockFlags->setBit(int(blockNumber));
+ }
+ blockNumbers.clear();
+ blockFlags->setBit(int(bb->index()));
+ } else {
+ blockNumbers.append(bb->index());
+ }
+ }
+
+ void remove(MIBlock *bb)
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags) {
+ blockFlags->clearBit(bb->index());
+ return;
+ }
+
+ for (int i = 0; i < blockNumbers.size(); ++i) {
+ if (blockNumbers[i] == bb->index()) {
+ blockNumbers.remove(i);
+ return;
+ }
+ }
+ }
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ void collectValues(std::vector<MIBlock *> &bbs) const;
+
+ bool contains(MIBlock *bb) const
+ {
+ Q_ASSERT(function);
+
+ if (blockFlags)
+ return blockFlags->at(bb->index());
+
+ for (unsigned int blockNumber : blockNumbers) {
+ if (blockNumber == bb->index())
+ return true;
+ }
+
+ return false;
+ }
+};
+
+class MIBlockSet::const_iterator
+{
+ const MIBlockSet &set;
+ // ### These two members could go into a union, but clang won't compile
+ // (https://codereview.qt-project.org/#change,74259)
+ QVarLengthArray<MIBlock::Index, 8>::const_iterator numberIt;
+ MIBlock::Index flagIt;
+
+ friend class MIBlockSet;
+ const_iterator(const MIBlockSet &set, bool end)
+ : set(set)
+ {
+ if (end || !set.function) {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.end();
+ else
+ flagIt = set.blockFlags->size();
+ } else {
+ if (!set.blockFlags)
+ numberIt = set.blockNumbers.begin();
+ else
+ findNextWithFlags(0);
+ }
+ }
+
+ void findNextWithFlags(int start)
+ {
+ flagIt = MIBlock::Index(set.blockFlags->findNext(start, true, /*wrapAround = */false));
+ Q_ASSERT(flagIt <= MIBlock::Index(set.blockFlags->size()));
+ }
+
+public:
+ MIBlock *operator*() const
+ {
+ if (!set.blockFlags)
+ return set.function->block(*numberIt);
+
+ Q_ASSERT(flagIt <= set.function->blockCount());
+ return set.function->block(flagIt);
+
+ }
+
+ bool operator==(const const_iterator &other) const
+ {
+ if (&set != &other.set)
+ return false;
+ if (!set.blockFlags)
+ return numberIt == other.numberIt;
+ return flagIt == other.flagIt;
+ }
+
+ bool operator!=(const const_iterator &other) const
+ { return !(*this == other); }
+
+ const_iterator &operator++()
+ {
+ if (!set.blockFlags)
+ ++numberIt;
+ else
+ findNextWithFlags(flagIt + 1);
+
+ return *this;
+ }
+};
+
+inline bool MIBlockSet::empty() const
+{ return begin() == end(); }
+
+inline MIBlockSet::const_iterator MIBlockSet::begin() const
+{ return const_iterator(*this, false); }
+
+inline MIBlockSet::const_iterator MIBlockSet::end() const
+{ return const_iterator(*this, true); }
+
+inline void MIBlockSet::collectValues(std::vector<MIBlock *> &bbs) const
+{
+ Q_ASSERT(function);
+
+ for (auto it : *this)
+ bbs.push_back(it);
+}
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4MIBLOCKSET_P_H
diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h
index 679a29764a..76065fb1bc 100644
--- a/src/qml/jit/qv4node_p.h
+++ b/src/qml/jit/qv4node_p.h
@@ -472,17 +472,6 @@ public:
m_worklist.push_back(n);
}
- void reEnqueueLate(Node *n)
- {
- if (!n)
- return;
- State &s = nodeState(n);
- if (s == Queued)
- return;
- s = Queued;
- m_worklist.insert(m_worklist.begin(), n);
- }
-
void enqueueAllInputs(Node *n)
{
for (Node *input : n->inputs())
@@ -519,19 +508,14 @@ public:
Node *n = m_worklist.back();
m_worklist.pop_back();
State &s = nodeState(n);
- if (s == Queued) {
- s = Visited;
- return n;
- }
- Q_UNREACHABLE();
+ Q_ASSERT(s == Queued);
+ s = Visited;
+ return n;
}
return nullptr;
}
- void markAsVisited(Node *n)
- { nodeState(n) = Visited; }
-
bool isVisited(Node *n) const
{ return nodeState(n) == Visited; }
diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp
index 8356a35098..acd5328fd0 100644
--- a/src/qml/jit/qv4operation.cpp
+++ b/src/qml/jit/qv4operation.cpp
@@ -223,11 +223,7 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLLoadScopeObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLStoreScopeObjectProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
- case K::QMLLoadContextObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLStoreContextObjectProperty: return get(3, 1, 1, 1, 1, 2, none, F::CanThrow);
- case K::QMLLoadIdObject: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
+ case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
@@ -263,8 +259,6 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure);
case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
- case K::QMLLoadContext: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
- case K::QMLLoadImportedScripts: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags);
@@ -423,7 +417,7 @@ QString ConstantPayload::debugString() const
QString ConstantPayload::debugString(QV4::Value v)
{
if (v.isManaged())
- return QString().sprintf("Ptr: %p", v.heapObject());
+ return QString::asprintf("Ptr: %p", v.heapObject());
if (v.isEmpty())
return QStringLiteral("empty");
return v.toQStringNoThrow();
@@ -518,18 +512,12 @@ static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissin
case K::JSDeleteName: return M<R::DeleteName>::doIt();
case K::JSIn: return M<R::In>::doIt();
case K::JSInstanceOf: return M<R::Instanceof>::doIt();
- case K::QMLLoadScopeObjectProperty: return M<R::LoadQmlScopeObjectProperty>::doIt();
- case K::QMLStoreScopeObjectProperty: return M<R::StoreQmlScopeObjectProperty>::doIt();
- case K::QMLLoadContextObjectProperty: return M<R::LoadQmlContextObjectProperty>::doIt();
- case K::QMLStoreContextObjectProperty: return M<R::StoreQmlContextObjectProperty>::doIt();
- case K::QMLLoadIdObject: return M<R::LoadQmlIdObject>::doIt();
+ case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt();
case K::JSTypeofName: return M<R::TypeofName>::doIt();
case K::JSTypeofValue: return M<R::TypeofValue>::doIt();
case K::JSDeclareVar: return M<R::DeclareVar>::doIt();
case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt();
- case K::QMLLoadContext: return M<R::LoadQmlContext>::doIt();
- case K::QMLLoadImportedScripts: return M<R::LoadQmlImportedScripts>::doIt();
case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt();
case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt();
case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt();
diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h
index 8b66e58d4b..43214023e8 100644
--- a/src/qml/jit/qv4operation_p.h
+++ b/src/qml/jit/qv4operation_p.h
@@ -123,12 +123,10 @@ enum OpKind: uint16_t {
JSDeleteName,
JSIn,
JSInstanceOf,
- /* ok, these are qml object ops, but we don't care for now and treat them a s JS */
- QMLLoadScopeObjectProperty,
- QMLStoreScopeObjectProperty,
- QMLLoadContextObjectProperty,
- QMLStoreContextObjectProperty,
- QMLLoadIdObject,
+
+ /* ok, these are qml object ops, but we don't care for now and treat them as JS */
+ QMLLoadQmlContextPropertyLookup,
+ QMLCallQmlContextPropertyLookup,
JSEqual,
JSGreaterThan,
@@ -168,16 +166,11 @@ enum OpKind: uint16_t {
JSCreateClass,
JSConstruct,
JSConstructWithSpread,
- /* ok, these are qml vararg calls, but we don't care for now and treat them as JS */
- QMLCallScopeObjectProperty,
- QMLCallContextObjectProperty,
JSTypeofName,
JSTypeofValue,
JSDeclareVar,
JSDestructureRestElement,
- QMLLoadContext,
- QMLLoadImportedScripts,
JSThisToObject,
JSCreateMappedArgumentsObject,
JSCreateUnmappedArgumentsObject,
diff --git a/src/qml/jit/qv4schedulers.cpp b/src/qml/jit/qv4schedulers.cpp
new file mode 100644
index 0000000000..0dffefa951
--- /dev/null
+++ b/src/qml/jit/qv4schedulers.cpp
@@ -0,0 +1,912 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qloggingcategory.h>
+
+#include "qv4schedulers_p.h"
+#include "qv4util_p.h"
+#include "qv4graph_p.h"
+#include "qv4blockscheduler_p.h"
+#include "qv4stackframe_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace IR {
+
+Q_LOGGING_CATEGORY(lcSched, "qt.v4.ir.scheduling")
+Q_LOGGING_CATEGORY(lcDotCFG, "qt.v4.ir.scheduling.cfg")
+
+static bool needsScheduling(Node *n)
+{
+ if (n->operation()->isConstant())
+ return false;
+ switch (n->opcode()) {
+ case Meta::Function: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Phi:
+ case Meta::EffectPhi:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool NodeScheduler::canStartBlock(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Start: Q_FALLTHROUGH();
+ case Meta::IfTrue:
+ case Meta::IfFalse:
+ case Meta::Region:
+ case Meta::HandleUnwind:
+ case Meta::OnException:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool NodeScheduler::isControlFlowSplit(Node *node) const
+{
+ int nOutputs = node->operation()->controlOutputCount();
+ if (nOutputs == 2) {
+ // if there is a "missing" control output, it's for exception flow without unwinder
+ int controlUses = 0;
+ auto uses = node->uses();
+ for (auto it = uses.begin(), eit = uses.end(); it != eit; ++it) {
+ if (isLive(*it) && it.isUsedAsControl())
+ ++controlUses;
+ }
+ return controlUses == 2;
+ }
+ return nOutputs > 2;
+}
+
+bool NodeScheduler::isBlockTerminator(Node *node) const
+{
+ switch (node->operation()->kind()) {
+ case Meta::Branch: Q_FALLTHROUGH();
+ case Meta::Jump:
+ case Meta::Return:
+ case Meta::TailCall:
+ case Meta::UnwindDispatch:
+ case Meta::End:
+ return true;
+ case Meta::Call:
+ return isControlFlowSplit(node);
+ default:
+ return false;
+ }
+}
+
+MIBlock *NodeScheduler::getCommonDominator(MIBlock *one, MIBlock *other) const
+{
+ MIBlock::Index a = one->index();
+ MIBlock::Index b = other->index();
+
+ while (a != b) {
+ if (m_dominatorDepthForBlock[a] < m_dominatorDepthForBlock[b])
+ b = m_domTree->immediateDominator(b);
+ else
+ a = m_domTree->immediateDominator(a);
+ }
+
+ return m_miFunction->block(a);
+}
+
+// For Nodes that end up inside loops, it'd be great if we can move (hoist) them out of the loop.
+// To do that, we need a block that preceeds the loop. (So the block before the loop header.)
+// This function calculates that hoist block if the original block is in a loop.
+MIBlock *NodeScheduler::getHoistBlock(MIBlock *block) const
+{
+ if (m_loopInfo->isLoopHeader(block))
+ return m_miFunction->block(m_domTree->immediateDominator(block->index()));
+
+ // make the loop header a candidate:
+ MIBlock *loopHeader = m_loopInfo->loopHeaderFor(block);
+ if (loopHeader == nullptr)
+ return nullptr; // block is not in a loop
+
+ // And now the tricky part: block has to dominate all exits from the loop. If it does not do
+ // that, it meanse that there is an exit from the loop that can be reached before block. In
+ // that case, hoisting from "block" to "loopHeader" would mean there now is an extra calculation
+ // that is not needed for a certain loop exit.
+ for (MIBlock *outEdge : m_loopInfo->loopExitsForLoop(loopHeader)) {
+ if (getCommonDominator(block, outEdge) != block)
+ return nullptr;
+ }
+
+ return m_miFunction->block(m_domTree->immediateDominator(loopHeader->index()));
+}
+
+NodeScheduler::NodeScheduler(Function *irFunction)
+ : m_irFunction(irFunction)
+ , m_vregs(irFunction->graph()->nodeCount(), std::numeric_limits<unsigned>::max())
+ , m_live(irFunction->graph(), /*collectUses =*/ false /* do explicitly NOT collect uses! */)
+{
+}
+
+MIFunction *NodeScheduler::buildMIFunction()
+{
+ m_miFunction = new MIFunction(m_irFunction);
+
+ // step 1: build the CFG
+ auto roots = buildCFG();
+ m_miFunction->renumberBlocks();
+ m_miFunction->dump(QStringLiteral("CFG after renumbering"));
+
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->index()
+ == MIFunction::StartBlockIndex);
+ Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->instructions().front().opcode()
+ == Meta::Start);
+
+ // step 2: build the dominator tree
+ if (lcDotCFG().isDebugEnabled())
+ dumpDotCFG();
+ m_domTree.reset(new DominatorTree(m_miFunction));
+ m_dominatorDepthForBlock = m_domTree->calculateNodeDepths();
+
+ // step 3: find loops
+ m_loopInfo.reset(new LoopInfo(*m_domTree.data()));
+ m_loopInfo->detectLoops();
+
+ // step 4: schedule early
+ scheduleEarly(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after early scheduling"));
+
+ // step 5: schedule late
+ scheduleLate(roots);
+ showNodesByBlock(QStringLiteral("nodes per block after late scheduling"));
+
+ // step 6: schedule instructions in each block
+ scheduleNodesInBlock();
+
+ m_miFunction->dump(QStringLiteral("MI before block scheduling"));
+
+ // step 7: order the basic blocks in the CFG
+ BlockScheduler blockScheduler(*m_domTree.data(), *m_loopInfo.data());
+ m_miFunction->setBlockOrder(blockScheduler.scheduledBlockSequence());
+
+ // we're done
+ m_miFunction->renumberInstructions();
+ m_miFunction->setVregCount(m_nextVReg);
+ m_miFunction->dump(QStringLiteral("MI after scheduling"));
+ return m_miFunction;
+}
+
+static Node *splitEdge(Function *irFunction, Node *node, unsigned inputIndex)
+{
+ Graph *g = irFunction->graph();
+ Node *in = node->input(inputIndex);
+ Node *region = g->createNode(g->opBuilder()->getRegion(1), &in, 1);
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &region, 1);
+
+ qCDebug(lcSched) << "splitting critical edge from node" << node->id()
+ << "to node" << node->input(inputIndex)->id()
+ << "by inserting jump node" << jump->id()
+ << "and region node" << region->id();
+
+ node->replaceInput(inputIndex, jump);
+ return jump;
+}
+
+// See Chapter 6.3.1 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+std::vector<Node *> NodeScheduler::buildCFG()
+{
+ std::vector<Node *> roots;
+ roots.reserve(32);
+ NodeWorkList todo(m_irFunction->graph());
+
+ auto enqueueControlInputs = [this, &todo](Node *node) {
+ for (unsigned i = 0, ei = node->operation()->controlInputCount(); i != ei; ++i) {
+ const auto inputIndex = node->operation()->indexOfFirstControl() + i;
+ Node *input = node->input(inputIndex);
+ Q_ASSERT(input);
+ if (node->operation()->kind() == Meta::Region
+ && node->operation()->controlInputCount() > 1
+ && isControlFlowSplit(input)) {
+ // critical edge!
+ input = splitEdge(m_irFunction, node, inputIndex);
+ m_live.markReachable(input);
+ m_live.markReachable(input->controlInput(0));
+ }
+ if (!isBlockTerminator(input)) {
+ auto g = m_irFunction->graph();
+ Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &input, 1);
+ node->replaceInput(inputIndex, jump);
+ m_live.markReachable(jump);
+ qCDebug(lcSched) << "inserting jump node" << jump->id()
+ << "between node" << node->id()
+ << "and node" << input->id();
+ input = jump;
+ }
+ todo.enqueue(input);
+ }
+ };
+
+ // create the CFG by scheduling control dependencies that start/end blocks:
+ todo.enqueue(m_irFunction->graph()->endNode());
+ while (Node *node = todo.dequeueNextNodeForVisiting()) {
+ Q_ASSERT(isBlockTerminator(node));
+
+ if (schedulerData(node)->minimumBlock)
+ continue;
+
+ MIBlock *b = m_miFunction->addBlock();
+
+ qCDebug(lcSched) << "scheduling node" << node->id() << "as terminator for new block"
+ << b->index();
+ b->instructions().push_front(createMIInstruction(node));
+ placeFixed(node, b, Schedule);
+ roots.push_back(node);
+
+ if (Node *framestate = node->frameStateInput()) {
+ placeFixed(framestate, b, DontSchedule);
+ qCDebug(lcSched) << ".. also scheduling framestate dependency node" << node->id()
+ << "in block" << b->index();
+ }
+
+ if (node->opcode() == Meta::End) {
+ enqueueControlInputs(node);
+ continue;
+ }
+
+ while (true) {
+ Node *controlDependency = node->controlInput(0);
+ if (!controlDependency)
+ break;
+ if (todo.isVisited(controlDependency))
+ break;
+ if (schedulerData(controlDependency)->isFixed)
+ break;
+
+ if (controlDependency->opcode() == Meta::Start) {
+ qCDebug(lcSched) << "placing start node" << controlDependency->id()
+ << "in block" << b->index();
+ handleStartNode(controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ break; // we're done with this block
+ }
+ if (isBlockTerminator(controlDependency)) {
+ qCDebug(lcSched) << "found terminator node" << controlDependency->id()
+ << "for another block, so finish block" << b->index();
+ Node *merge = m_irFunction->graph()->createNode(
+ m_irFunction->graph()->opBuilder()->getRegion(1), &controlDependency, 1);
+ node->replaceInput(node->operation()->indexOfFirstControl(), merge);
+ addBlockStart(roots, merge, b);
+ placeFixed(merge, b, Schedule);
+ m_live.markReachable(merge);
+ todo.enqueue(controlDependency);
+ break; // we're done with this block
+ }
+ if (canStartBlock(controlDependency)
+ || schedulerData(controlDependency->controlInput())->isFixed) {
+ qCDebug(lcSched) << "found block start node" << controlDependency->id()
+ << "for this block, so finish block" << b->index();
+ addBlockStart(roots, controlDependency, b);
+ placeFixed(controlDependency, b, Schedule);
+ roots.push_back(controlDependency);
+ enqueueControlInputs(controlDependency);
+ break; // we're done with this block
+ }
+ qCDebug(lcSched) << "skipping node" << controlDependency->id();
+ node = controlDependency;
+ }
+ }
+
+ // link the edges of the MIBlocks, and add basic-block arguments:
+ for (MIBlock *toBlock : m_miFunction->blocks()) {
+ Q_ASSERT(!toBlock->instructions().empty());
+ MIInstr &instr = toBlock->instructions().front();
+ Node *toNode = instr.irNode();
+ const auto opcode = toNode->operation()->kind();
+ if (opcode == Meta::Region) {
+ unsigned inputNr = 0;
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ MIInstr &fromTerminator = fromBlock->instructions().back();
+ if (fromTerminator.irNode()->opcode() == Meta::Jump ||
+ fromTerminator.irNode()->opcode() == Meta::UnwindDispatch) {
+ unsigned arg = 0;
+ for (const MIOperand &bbArg : toBlock->arguments()) {
+ fromTerminator.setOperand(arg++,
+ createMIOperand(bbArg.irNode()->input(inputNr)));
+ }
+ }
+ ++inputNr;
+ }
+ } else if (opcode == Meta::End) {
+ for (Node *input : toNode->inputs()) {
+ MIBlock *fromBlock = schedulerData(input)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ } else if (Node *fromNode = toNode->controlInput()) {
+ MIBlock *fromBlock = schedulerData(fromNode)->minimumBlock;
+ fromBlock->addOutEdge(toBlock);
+ toBlock->addInEdge(fromBlock);
+ }
+ }
+
+ m_irFunction->dump(QStringLiteral("graph after building CFG"));
+
+ auto startBlock = schedulerData(m_irFunction->graph()->startNode())->minimumBlock;
+ m_miFunction->setStartBlock(startBlock);
+
+ if (lcSched().isDebugEnabled())
+ m_miFunction->dump(QStringLiteral("control flow graph before renumbering"));
+ m_miFunction->verifyCFG();
+
+ return roots;
+}
+
+// See Chapter 6.3.3 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+void NodeScheduler::scheduleEarly(const std::vector<Node *> &roots)
+{
+ // scheduling one node might have the effect of queueing its dependencies
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots) {
+ todo.enqueue(root);
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleEarly(node, todo);
+ }
+}
+
+void NodeScheduler::scheduleEarly(Node *node, NodeWorkList &todo)
+{
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "early...";
+
+ SchedulerData *sd = schedulerData(node);
+
+ if (sd->isFixed) {
+ // Fixed nodes already know their schedule early position.
+ qCDebug(lcSched) << ".. Fixed node" << node->id() << "is on minimum block"
+ << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[sd->minimumBlock->index()];
+ }
+
+ for (Node *use : node->uses()) {
+ if (isLive(use))
+ propagateMinimumPosition(sd->minimumBlock, use, todo);
+ else
+ qCDebug(lcSched) << ".. Skipping node" << use->id() << "as it's not live";
+ }
+}
+
+void NodeScheduler::propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(newMinimumPosition);
+
+ SchedulerData *sd = schedulerData(toNode);
+ if (sd->isFixed) // nothing to do
+ return;
+
+ MIBlock::Index minimumBlockIndex = sd->minimumBlock
+ ? sd->minimumBlock->index()
+ : MIFunction::StartBlockIndex;
+ Q_ASSERT(m_domTree->insideSameDominatorChain(newMinimumPosition->index(), minimumBlockIndex));
+ if (sd->minimumBlock == nullptr
+ || m_dominatorDepthForBlock[newMinimumPosition->index()]
+ > m_dominatorDepthForBlock[minimumBlockIndex]) {
+ // ok, some input for toNode is scheduled *after* our current minimum depth, so we need
+ // to adjust out minimal position. (This might involve rescheduling toNode's uses.)
+ place(toNode, newMinimumPosition);
+ todo.reEnqueue(toNode);
+ qCDebug(lcSched) << ".. Propagating minimum block" << sd->minimumBlock->index()
+ << "which has dominator depth"
+ << m_dominatorDepthForBlock[newMinimumPosition->index()]
+ << "to use node" << toNode->id();
+ } else {
+ qCDebug(lcSched) << ".. Minimum position" << newMinimumPosition->index()
+ << "is not better than" << minimumBlockIndex
+ << "for node" << toNode->id();
+ }
+}
+
+// See Chapter 6.3.4 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for
+// a description of the algorithm.
+//
+// There is one extra detail not described in the thesis mentioned above: loop hoisting. Before we
+// place a node in the latest block that dominates all uses, we check if we accidentally sink it
+// *into* a loop (meaning the latest block is inside a loop, where it is not if the earliest
+// possible block would be chosen). If we detect that a nodes is going to sink into a loop, we walk
+// the dominator path from the latest block up to the earliest block, and pick the first block that
+// is in the same loop (if any) as the earlieast block.
+//
+// As noted in the thesis, this strategy might enlongen life times, which could be harmful for
+// values that are simple to re-materialized or re-calculate.
+void NodeScheduler::scheduleLate(const std::vector<Node *> &roots)
+{
+ NodeWorkList todo(m_irFunction->graph());
+ for (Node *root : roots)
+ todo.enqueue(root);
+
+ while (Node *node = todo.dequeueNextNodeForVisiting())
+ scheduleNodeLate(node, todo);
+}
+
+void NodeScheduler::scheduleNodeLate(Node *node, NodeWorkList &todo)
+{
+ if (!needsScheduling(node))
+ return;
+ qCDebug(lcSched) << "Scheduling node" << node->id() << "late...";
+
+ auto sd = schedulerData(node);
+ if (sd->unscheduledUses == SchedulerData::NotYetCalculated) {
+ sd->unscheduledUses = 0;
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isFixed)
+ continue;
+ todo.enqueue(use);
+ ++sd->unscheduledUses;
+ }
+ }
+
+ if (sd->isFixed) {
+ qCDebug(lcSched) << ".. it's fixed";
+ enqueueInputs(node, todo);
+ return;
+ }
+
+ if (sd->unscheduledUses) {
+ qCDebug(lcSched).noquote() << ".. not all uses are fixed, postpone it."<< todo.status(node);
+ return;
+ }
+
+ MIBlock *&minBlock = sd->minimumBlock;
+ if (minBlock == nullptr)
+ minBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ MIBlock *commonUseDominator = commonDominatorOfUses(node);
+ qCDebug(lcSched) << ".. common use dominator: block" << commonUseDominator->index();
+
+ // the minBlock has to dominate the uses, *and* the common dominator of the uses.
+ Q_ASSERT(minBlock->index() == commonUseDominator->index() ||
+ m_domTree->dominates(minBlock->index(), commonUseDominator->index()));
+
+ // we now found the deepest block, so use it as the target block:
+ MIBlock *targetBlock = commonUseDominator;
+
+ if (node->opcode() == Meta::FrameState) {
+ // never hoist framestates: they're used (among other things) to keep their inputs alive, so
+ // hoisting them out would end the life-time of those inputs prematurely
+ } else {
+ // but we want to prevent blocks sinking into loops unnecessary
+ MIBlock *hoistBlock = getHoistBlock(targetBlock);
+ while (hoistBlock
+ && m_dominatorDepthForBlock[hoistBlock->index()]
+ >= m_dominatorDepthForBlock[minBlock->index()]) {
+ qCDebug(lcSched) << ".. hoisting node" << node->id() << "from block"
+ << targetBlock->index() << "to block" << hoistBlock->index();
+ // ok, so there a) is a hoist block and b) it's deeper than the minimum block,
+ // so lift it up one level ...
+ targetBlock = hoistBlock;
+ // ... and see if we can lift it one more level
+ hoistBlock = getHoistBlock(targetBlock);
+ }
+ }
+
+ qCDebug(lcSched) << ".. fixating it in block" << targetBlock->index()
+ << "where the minimum block was" << minBlock->index();
+
+ placeFixed(node, targetBlock, DontSchedule);
+ enqueueInputs(node, todo);
+}
+
+void NodeScheduler::enqueueInputs(Node *node, NodeWorkList &todo)
+{
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto sd = schedulerData(input);
+ if (sd->isFixed)
+ continue;
+ qCDebug(lcSched).noquote() << "... enqueueing input node" << input->id()
+ << todo.status(input);
+ if (sd->unscheduledUses != SchedulerData::NotYetCalculated) {
+ if (sd->unscheduledUses > 0)
+ --sd->unscheduledUses;
+ if (sd->unscheduledUses == 0)
+ todo.reEnqueue(input);
+ } else {
+ todo.reEnqueue(input);
+ }
+ }
+}
+
+Node *NodeScheduler::firstNotFixedUse(Node *node)
+{
+ for (Node *use : node->uses()) {
+ if (!isLive(use))
+ continue;
+ if (!schedulerData(use)->isFixed)
+ return use;
+ }
+ return nullptr;
+}
+
+MIBlock *NodeScheduler::commonDominatorOfUses(Node *node)
+{
+ MIBlock *commonDominator = nullptr;
+ for (auto useIt = node->uses().begin(), useEIt = node->uses().end(); useIt != useEIt; ++useIt) {
+ Node *use = *useIt;
+ if (!isLive(use))
+ continue;
+ // region nodes use other nodes through their control dependency. But those nodes should
+ // already have been placed as block terminators before.
+ Q_ASSERT(use->opcode() != Meta::Region);
+ if (use->opcode() == Meta::Phi || use->opcode() == Meta::EffectPhi) {
+ // find the predecessor block defining this input
+ Node *region = use->controlInput(0);
+ Node *input = region->controlInput(useIt.inputIndex());
+ use = input;
+ }
+ auto minBlock = schedulerData(use)->minimumBlock;
+ if (commonDominator == nullptr)
+ commonDominator = minBlock;
+ else
+ commonDominator = getCommonDominator(commonDominator, minBlock);
+ }
+ return commonDominator;
+}
+
+void NodeScheduler::scheduleNodesInBlock()
+{
+ auto startBlock = m_miFunction->block(MIFunction::StartBlockIndex);
+ for (Node *n : m_live.reachable()) {
+ auto sd = schedulerData(n);
+ if (!sd->minimumBlock)
+ sd->minimumBlock = startBlock;
+ }
+
+ std::vector<std::vector<SchedulerData *>> nodesForBlock;
+ nodesForBlock.resize(m_miFunction->blockCount());
+
+ for (auto sd : m_schedulerData) {
+ if (sd == nullptr)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ sd->unscheduledUses = 0;
+ for (Node *use : sd->node->uses()) {
+ if (!needsScheduling(use))
+ continue;
+ if (schedulerData(use)->isScheduledInBlock)
+ continue;
+ if (schedulerData(use)->minimumBlock == sd->minimumBlock)
+ ++sd->unscheduledUses;
+ }
+ if (sd->unscheduledUses == 0)
+ nodesForBlock[sd->minimumBlock->index()].push_back(sd);
+ }
+
+ NodeWorkList todo(m_irFunction->graph());
+ for (MIBlock *b : m_miFunction->blocks()) {
+ qCDebug(lcSched) << "Scheduling inside block" << b->index();
+ MIInstr *insertionPoint = &b->instructions().back();
+ todo.enqueue(insertionPoint->irNode());
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ for (auto sd : nodesForBlock[b->index()]) {
+ if (!sd->isScheduledInBlock)
+ todo.enqueue(sd->node);
+ }
+ scheduleNodesInBlock(insertionPoint, b, todo);
+ Q_ASSERT(todo.isEmpty());
+ todo.reset();
+ }
+}
+
+void NodeScheduler::scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo)
+{
+ while (Node *n = todo.dequeueNextNodeForVisiting())
+ scheduleNodeInBlock(n, insertionPoint, b, todo);
+}
+
+void NodeScheduler::scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b,
+ NodeWorkList &todo)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (!isLive(node))
+ return;
+
+ if (!needsScheduling(node))
+ return;
+
+ auto nodeData = schedulerData(node);
+ if (nodeData->minimumBlock != b)
+ return;
+
+ const bool wasAlreadyScheduled = nodeData->isScheduledInBlock;
+ if (!wasAlreadyScheduled) {
+ if (nodeData->unscheduledUses)
+ return;
+
+ scheduleNodeNow(node, insertionPoint);
+ }
+
+ if (Node *framestate = node->frameStateInput())
+ scheduleNodeInBlock(framestate, insertionPoint, b, todo);
+
+ for (Node *input : node->inputs()) {
+ if (!input)
+ continue;
+ if (!needsScheduling(input))
+ continue;
+ if (!isLive(input))
+ continue;
+ auto inputInfo = schedulerData(input);
+ if (inputInfo->isScheduledInBlock)
+ continue;
+ Q_ASSERT(inputInfo->minimumBlock != nullptr);
+ if (inputInfo->minimumBlock != b)
+ continue;
+ Q_ASSERT(!input->isDead());
+ Q_ASSERT(inputInfo->unscheduledUses != SchedulerData::NotYetCalculated);
+ if (!wasAlreadyScheduled && inputInfo->unscheduledUses > 0)
+ --inputInfo->unscheduledUses;
+ if (inputInfo->unscheduledUses == 0)
+ todo.enqueue(input);
+ }
+}
+
+void NodeScheduler::scheduleNodeNow(Node *node, MIInstr *&insertionPoint)
+{
+ qCDebug(lcSched) << ".. scheduling node" << node->id()
+ << "in block" << insertionPoint->parent()->index()
+ << "before node" << insertionPoint->irNode()->id();
+
+ MIInstr *newInstr = createMIInstruction(node);
+ newInstr->insertBefore(insertionPoint);
+ insertionPoint = newInstr;
+}
+
+void NodeScheduler::place(Node *node, MIBlock *b)
+{
+ Q_ASSERT(!node->isDead());
+
+ if (b == nullptr)
+ return;
+
+ schedulerData(node)->minimumBlock = b;
+}
+
+void NodeScheduler::placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled)
+{
+ place(node, b);
+ auto sd = schedulerData(node);
+ Q_ASSERT(!sd->isFixed);
+ sd->isFixed = true;
+ sd->isScheduledInBlock = markScheduled == Schedule;
+}
+
+unsigned NodeScheduler::vregForNode(Node *node)
+{
+ unsigned &vreg = m_vregs[unsigned(node->id())];
+ if (vreg == std::numeric_limits<unsigned>::max())
+ vreg = m_nextVReg++;
+ return vreg;
+}
+
+void NodeScheduler::addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block)
+{
+ block->instructions().insert(block->instructions().begin(), createMIInstruction(startNode));
+ if (startNode->opcode() == Meta::Region) {
+ for (Node *use : startNode->uses()) {
+ if (use->opcode() == Meta::Phi && isLive(use)) {
+ block->addArgument(MIOperand::createVirtualRegister(use, vregForNode(use)));
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ } else if (use->opcode() == Meta::EffectPhi && isLive(use)) {
+ placeFixed(use, block, Schedule);
+ roots.push_back(use);
+ }
+ }
+ }
+}
+
+void NodeScheduler::handleStartNode(Node *startNode, MIBlock *startBlock)
+{
+ startBlock->instructions().push_front(createMIInstruction(startNode));
+
+ QVarLengthArray<Node *, 32> args;
+ for (Node *use : startNode->uses()) {
+ switch (use->opcode()) {
+ case Meta::Engine: Q_FALLTHROUGH();
+ case Meta::CppFrame:
+ case Meta::Function:
+ placeFixed(use, startBlock, Schedule);
+ break;
+ case Meta::Parameter: {
+ auto param = ParameterPayload::get(*use->operation());
+ int idx = int(param->parameterIndex());
+ if (args.size() <= idx)
+ args.resize(idx + 1);
+ args[int(idx)] = use;
+ placeFixed(use, startBlock, Schedule);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (unsigned i = 0, ei = unsigned(args.size()); i != ei; ++i) {
+ if (Node *arg = args.at(int(i)))
+ startBlock->addArgument(MIOperand::createJSStackSlot(arg, i));
+ }
+}
+
+static Node *firstControlOutput(Node *n)
+{
+ for (auto it = n->uses().begin(), eit = n->uses().end(); it != eit; ++it) {
+ if (it.isUsedAsControl())
+ return *it;
+ }
+ return nullptr;
+}
+
+MIInstr *NodeScheduler::createMIInstruction(Node *node)
+{
+ const auto opcode = node->operation()->kind();
+
+ unsigned nArgs = 0;
+ switch (opcode) {
+ case Meta::UnwindDispatch: Q_FALLTHROUGH();
+ case Meta::Jump: {
+ Node *target = firstControlOutput(node);
+ if (target->opcode() == Meta::Region) {
+ for (Node *n : target->uses()) {
+ if (n->opcode() == Meta::Phi && isLive(n))
+ ++nArgs;
+ }
+ }
+ }
+ break;
+ case Meta::Branch:
+ nArgs = 1;
+ break;
+ case Meta::Return:
+ nArgs = 1;
+ break;
+ default:
+ nArgs = node->operation()->valueInputCount();
+ break;
+ }
+
+ MIInstr *instr = MIInstr::create(m_irFunction->pool(), node, nArgs);
+ for (unsigned i = 0, ei = node->operation()->valueInputCount(); i != ei; ++i)
+ instr->setOperand(i, createMIOperand(node->input(i)));
+ if (node->opcode() != Meta::Start && node->operation()->valueOutputCount() > 0)
+ instr->setDestination(createMIOperand(node));
+
+ schedulerData(node)->isScheduledInBlock = true;
+ return instr;
+}
+
+MIOperand NodeScheduler::createMIOperand(Node *node)
+{
+ if (node->operation()->isConstant())
+ return MIOperand::createConstant(node);
+
+ auto opcode = node->operation()->kind();
+ switch (opcode) {
+ case Meta::Parameter:
+ return MIOperand::createJSStackSlot(
+ node, unsigned(ParameterPayload::get(*node->operation())->parameterIndex()));
+ case Meta::Engine:
+ return MIOperand::createEngineRegister(node);
+ case Meta::CppFrame:
+ return MIOperand::createCppFrameRegister(node);
+ case Meta::Function:
+ return MIOperand::createFunction(node);
+ default:
+ if ((node->opcode() == Meta::Call
+ && CallPayload::get(*node->operation())->callee() == Meta::JSThisToObject)
+ || node->opcode() == Meta::StoreThis) {
+ return MIOperand::createJSStackSlot(node, CallData::This);
+ } else {
+ return MIOperand::createVirtualRegister(node, vregForNode(node));
+ }
+ }
+}
+
+void NodeScheduler::showNodesByBlock(const QString &description) const
+{
+ if (!lcSched().isDebugEnabled())
+ return;
+
+ qCDebug(lcSched) << description;
+
+ for (MIBlock *b : m_miFunction->blocks()) {
+ QString s;
+ for (const SchedulerData *sd : m_schedulerData) {
+ if (!sd)
+ continue;
+ if (!isLive(sd->node))
+ continue;
+ if (sd->minimumBlock == b) {
+ if (!s.isEmpty())
+ s += QStringLiteral(", ");
+ s += QStringLiteral("%1 (%2)").arg(QString::number(sd->node->id()),
+ sd->node->operation()->debugString());
+ }
+ }
+ if (s.isEmpty())
+ s = QStringLiteral("<<none>>");
+ qCDebug(lcSched, "Nodes in block %u: %s", b->index(), s.toUtf8().constData());
+ }
+}
+
+void NodeScheduler::dumpDotCFG() const
+{
+ QString out;
+ out += QLatin1Char('\n');
+ out += QStringLiteral("digraph{root=\"L%1\" label=\"Control Flow Graph\";"
+ "node[shape=circle];edge[dir=forward fontsize=10]\n")
+ .arg(MIFunction::StartBlockIndex);
+ for (MIBlock *src : m_miFunction->blocks()) {
+ for (MIBlock *dst : src->outEdges()) {
+ out += QStringLiteral("L%1->L%2\n").arg(QString::number(src->index()),
+ QString::number(dst->index()));
+ }
+ }
+ out += QStringLiteral("}\n");
+ qCDebug(lcDotCFG).nospace().noquote() << out;
+}
+
+} // IR namespace
+} // QV4 namespace
+QT_END_NAMESPACE
diff --git a/src/qml/jit/qv4schedulers_p.h b/src/qml/jit/qv4schedulers_p.h
new file mode 100644
index 0000000000..f9179816df
--- /dev/null
+++ b/src/qml/jit/qv4schedulers_p.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SCHEDULER_P_H
+#define QV4SCHEDULER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4global_p.h"
+#include "qv4mi_p.h"
+#include "qv4node_p.h"
+#include "qv4domtree_p.h"
+#include "qv4loopinfo_p.h"
+
+QT_REQUIRE_CONFIG(qml_tracing);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace IR {
+
+// Node scheduling "flattens" the graph into basic blocks with an ordered list of instructions.
+//
+// The various steps are mentioned in buildMIFunction, but the general idea is described in
+// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf in chapter 6.
+class NodeScheduler final
+{
+ Q_DISABLE_COPY_MOVE(NodeScheduler)
+
+ class SchedulerData final {
+ Q_DISABLE_COPY_MOVE(SchedulerData)
+ public:
+ static SchedulerData *create(QQmlJS::MemoryPool *pool)
+ { return pool->New<SchedulerData>(); }
+
+ SchedulerData() = default;
+ ~SchedulerData() = default;
+
+ Node *node = nullptr;
+ MIBlock *minimumBlock = nullptr;
+ bool isFixed = false;
+ bool isScheduledInBlock = false;
+ static constexpr unsigned NotYetCalculated = std::numeric_limits<unsigned>::max();
+ unsigned unscheduledUses = NotYetCalculated;
+ };
+
+public:
+ NodeScheduler(Function *irFunction);
+ ~NodeScheduler() = default;
+
+ MIFunction *buildMIFunction();
+
+private:
+ std::vector<Node *> buildCFG();
+ void scheduleEarly(const std::vector<Node *> &roots);
+ void scheduleEarly(Node *node, NodeWorkList &todo);
+ void propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, NodeWorkList &todo);
+ void scheduleLate(const std::vector<Node *> &roots);
+ void scheduleNodeLate(Node *node, NodeWorkList &todo);
+ void enqueueInputs(Node *node, NodeWorkList &todo);
+ Node *firstNotFixedUse(Node *node);
+ MIBlock *commonDominatorOfUses(Node *node);
+ void scheduleNodesInBlock();
+ void scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo);
+ void scheduleNodeNow(Node *node, MIInstr *&insertionPoint);
+
+ void place(Node *node, MIBlock *b);
+ enum ScheduleOrNot { DontSchedule, Schedule };
+ void placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled);
+ unsigned vregForNode(Node *node);
+ void addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block);
+ void enqueueControlInputs(Node *node);
+ void handleStartNode(Node *startNode, MIBlock *startBlock);
+ MIInstr *createMIInstruction(Node *node);
+ MIOperand createMIOperand(Node *node);
+ SchedulerData *schedulerData(Node *n)
+ {
+ if (Q_UNLIKELY(m_schedulerData.size() <= n->id()))
+ m_schedulerData.resize(n->id() + 8);
+ SchedulerData *&sd = m_schedulerData[n->id()];
+ if (Q_UNLIKELY(sd == nullptr)) {
+ sd = SchedulerData::create(m_irFunction->pool());
+ sd->node = n;
+ }
+ return sd;
+ }
+ bool isLive(Node *n) const
+ { return m_live.isReachable(n->id()); }
+ bool canStartBlock(Node *node) const;
+ bool isControlFlowSplit(Node *node) const;
+ bool isBlockTerminator(Node *node) const;
+ MIBlock *getCommonDominator(MIBlock *one, MIBlock *other) const;
+ MIBlock *getHoistBlock(MIBlock *block) const;
+
+ void showNodesByBlock(const QString &description) const;
+
+ void dumpDotCFG() const;
+
+private:
+ Function *m_irFunction = nullptr;
+ MIFunction *m_miFunction = nullptr;
+ QScopedPointer<LoopInfo> m_loopInfo;
+ QScopedPointer<DominatorTree> m_domTree;
+ std::vector<int> m_dominatorDepthForBlock;
+ std::vector<unsigned> m_vregs;
+ std::vector<SchedulerData *> m_schedulerData;
+ NodeCollector m_live;
+ unsigned m_nextVReg = 0;
+};
+
+} // namespace IR
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4SCHEDULER_P_H
diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp
index ded2488905..c8974b3a1b 100644
--- a/src/qml/jit/qv4tracingjit.cpp
+++ b/src/qml/jit/qv4tracingjit.cpp
@@ -41,6 +41,9 @@
#include "qv4vme_moth_p.h"
#include "qv4graphbuilder_p.h"
+#include "qv4lowering_p.h"
+#include "qv4mi_p.h"
+#include "qv4schedulers_p.h"
QT_BEGIN_NAMESPACE
@@ -73,6 +76,15 @@ void Moth::runTracingJit(QV4::Function *function)
IR::GraphBuilder::buildGraph(&irFunction);
irFunction.dump(QStringLiteral("initial IR"));
irFunction.verify();
+
+ IR::GenericLowering(irFunction).lower();
+ irFunction.dump(QStringLiteral("after generic lowering"));
+ irFunction.verify();
+
+ IR::NodeScheduler scheduler(&irFunction);
+ QScopedPointer<IR::MIFunction> miFunction(scheduler.buildMIFunction());
+ miFunction->dump(QStringLiteral("initial MI"));
+ irFunction.verify();
}
} // QV4 namespace
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index e20317cff1..e0bd986920 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -51,7 +51,6 @@
#include "qv4variantobject_p.h"
#include "qv4regexpobject_p.h"
#include "qv4errorobject_p.h"
-#include "private/qv8engine_p.h"
#include <private/qv4mm_p.h>
#include <private/qv4jscall_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -1368,7 +1367,7 @@ QObject *QJSValue::toQObject() const
\since 5.8
* If this QJSValue is a QMetaObject, returns the QMetaObject pointer
- * that the QJSValue represents; otherwise, returns 0.
+ * that the QJSValue represents; otherwise, returns \nullptr.
*
* \sa isQMetaObject()
*/
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 4a21f62cf2..98e0ef9e70 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -116,6 +116,9 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
args->fullyCreate();
+ if (!id.isArrayIndex())
+ return Object::virtualDefineOwnProperty(m, id, desc, attrs);
+
uint index = id.asArrayIndex();
if (!args->isMapped(index))
@@ -148,36 +151,42 @@ bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const
ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- uint index = id.asArrayIndex();
- if (index < args->d()->argCount && !args->d()->fullyCreated) {
- if (hasProperty)
- *hasProperty = true;
- return args->context()->args()[index].asReturnedValue();
+ if (id.isArrayIndex()) {
+ const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
+ uint index = id.asArrayIndex();
+ if (index < args->d()->argCount && !args->d()->fullyCreated) {
+ if (hasProperty)
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
+ }
+
+ if (args->isMapped(index)) {
+ Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
+ if (hasProperty)
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
+ }
}
- if (!args->isMapped(index))
- return Object::virtualGet(m, id, receiver, hasProperty);
- Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
- if (hasProperty)
- *hasProperty = true;
- return args->context()->args()[index].asReturnedValue();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- uint index = id.asArrayIndex();
-
- if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) {
- args->context()->setArg(index, value);
- return true;
+ if (id.isArrayIndex()) {
+ ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+ uint index = id.asArrayIndex();
+
+ if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) {
+ args->context()->setArg(index, value);
+ return true;
+ }
+
+ bool isMapped = (args == receiver && args->isMapped(index));
+ if (isMapped)
+ args->context()->setArg(index, value);
}
- bool isMapped = (args == receiver && args->isMapped(index));
- if (isMapped)
- args->context()->setArg(index, value);
-
return Object::virtualPut(m, id, value, receiver);
}
@@ -186,13 +195,16 @@ bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id)
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
args->fullyCreate();
bool result = Object::virtualDeleteProperty(m, id);
- if (result)
+ if (result && id.isArrayIndex())
args->removeMapping(id.asArrayIndex());
return result;
}
PropertyAttributes ArgumentsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
+ if (!id.isArrayIndex())
+ return Object::virtualGetOwnProperty(m, id, p);
+
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
uint index = id.asArrayIndex();
if (index < args->d()->argCount && !args->d()->fullyCreated) {
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index b975d02d9d..b3e607d74a 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -103,7 +103,9 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1);
ctor->addSymbolSpecies();
- ScopedObject unscopables(scope, engine->newObject(engine->classes[EngineBase::Class_Empty]->changeVTable(QV4::Object::staticVTable())));
+ Scoped<InternalClass> ic(scope, engine->classes[EngineBase::Class_Empty]
+ ->changeVTable(QV4::Object::staticVTable()));
+ ScopedObject unscopables(scope, engine->newObject(ic->d()));
ScopedString name(scope);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index c04617dac0..18927c637c 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -470,11 +470,17 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global);
jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global);
jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
- jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()));
- jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()));
+
+ ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(ic);
+ ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(ic);
+ ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(ic);
+ ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(ic);
+ ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
+ jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
str = newString(QStringLiteral("get [Symbol.species]"));
jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
@@ -1076,6 +1082,12 @@ extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext)
return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext));
}
+extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine)
+{
+ auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine));
+ return v4StackTrace(engine->currentContext());
+}
+
QUrl ExecutionEngine::resolvedUrl(const QString &file)
{
QUrl src(file);
@@ -1189,6 +1201,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
return throwError(error);
}
+ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
+{
+ Scope scope(this);
+ QString msg = name + QLatin1String(" is not defined");
+ ScopedObject error(scope, newReferenceErrorObject(msg));
+ return throwError(error);
+}
+
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);
@@ -1302,7 +1322,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
&& !value.as<ArrayObject>() && !value.as<FunctionObject>()) {
return QVariant::fromValue(QV4::JsonObject::toJsonObject(object));
} else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) {
- return qVariantFromValue<QObject *>(wrapper->object());
+ return QVariant::fromValue<QObject *>(wrapper->object());
} else if (object->as<QV4::QQmlContextWrapper>()) {
return QVariant();
} else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) {
@@ -1333,7 +1353,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
}
}
- return qVariantFromValue<QList<QObject*> >(list);
+ return QVariant::fromValue<QList<QObject*> >(list);
} else if (typeHint == QMetaType::QJsonArray) {
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
@@ -1524,6 +1544,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
case QMetaType::QLocale:
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
#endif
+ case QMetaType::QPixmap:
+ case QMetaType::QImage:
+ // Scarce value types
+ return QV4::Encode(newVariantObject(variant));
default:
break;
}
@@ -1639,9 +1663,8 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian
s = v4->newIdentifier(it.key());
key = s->propertyKey();
v = variantToJS(v4, it.value());
- uint idx = key->asArrayIndex();
- if (idx < UINT_MAX)
- o->arraySet(idx, v);
+ if (key->isArrayIndex())
+ o->arraySet(key->asArrayIndex(), v);
else
o->insertMember(s, v);
}
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 3735c24601..6df4545014 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -572,6 +572,7 @@ public:
ReturnedValue throwTypeError();
ReturnedValue throwTypeError(const QString &message);
ReturnedValue throwReferenceError(const Value &value);
+ ReturnedValue throwReferenceError(const QString &name);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column);
ReturnedValue throwRangeError(const Value &value);
ReturnedValue throwRangeError(const QString &message);
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 7702939e23..1bd4329fe8 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -96,7 +96,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
, codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
- , hasQmlDependencies(function->hasQmlDependencies())
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 374e46b929..f8125a58f8 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -94,7 +94,6 @@ public:
Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
- bool hasQmlDependencies;
bool isEval = false;
static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
index 566db6fd4e..14caa6953f 100644
--- a/src/qml/jsruntime/qv4generatorobject.cpp
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -139,7 +139,9 @@ void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedValue v(scope);
- ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype())));
+ Scoped<InternalClass> ic(scope, engine->newInternalClass(
+ Object::staticVTable(), engine->functionPrototype()));
+ ScopedObject ctorProto(scope, engine->newObject(ic->d()));
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 44adac26cd..d47393b3bb 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -94,7 +94,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); }
#elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \
&& (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD))
# define V4_ENABLE_JIT
-#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4)
+#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) \
+ && (defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_INTEGRITY))
# if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4)
# define V4_ENABLE_JIT
# elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2 // clang 3.5 and later will set this if the core supports the Thumb-2 ISA.
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index e476baa886..ae937b2889 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -70,7 +70,7 @@ IdentifierTable::~IdentifierTable()
{
free(entriesByHash);
free(entriesById);
- for (auto &h : idHashes)
+ for (const auto &h : qAsConst(idHashes))
h->identifierTable = nullptr;
}
@@ -216,9 +216,8 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str)
Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const
{
- uint arrayIdx = i.asArrayIndex();
- if (arrayIdx < UINT_MAX)
- return engine->newString(QString::number(arrayIdx));
+ if (i.isArrayIndex())
+ return engine->newString(QString::number(i.asArrayIndex()));
if (!i.isValid())
return nullptr;
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index 36569b0a60..3c732bc555 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -214,7 +214,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
if (argc >= 2 && argv[1].as<QV4::FunctionObject>())
callbackFunction = argv[1];
-#if QT_CONFIG(qml_network)
QUrl url(scope.engine->resolvedUrl(argv[0].toQStringNoThrow()));
if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor())
url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile);
@@ -225,9 +224,13 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext());
if (localFile.isEmpty()) {
+#if QT_CONFIG(qml_network)
QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction);
result = i->result();
-
+#else
+ result = resultValue(scope.engine, NetworkError);
+ callback(callbackFunction, result);
+#endif
} else {
QScopedPointer<QV4::Script> script;
QString error;
@@ -252,12 +255,6 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
callback(callbackFunction, result);
}
-#else
- QV4::ScopedValue result(scope);
- result = resultValue(scope.engine, NetworkError);
- callback(callbackFunction, result);
-#endif
-
return result->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index ddb8542e07..a10fda79f2 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -44,6 +44,7 @@
#include "qv4object_p.h"
#include "qv4identifiertable_p.h"
#include "qv4value_p.h"
+#include "qv4mm_p.h"
QT_BEGIN_NAMESPACE
@@ -144,7 +145,7 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons
data(nullptr)
{
if (other.alloc()) {
- int s = other.size();
+ const uint s = other.size();
data = MemberData::allocate(engine, other.alloc(), other.data);
setSize(s);
}
@@ -163,8 +164,8 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons
void SharedInternalClassDataPrivate<PropertyKey>::grow()
{
- uint a = alloc() * 2;
- int s = size();
+ const uint a = alloc() * 2;
+ const uint s = size();
data = MemberData::allocate(engine, a, data);
setSize(s);
Q_ASSERT(alloc() >= a);
@@ -204,7 +205,70 @@ void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s)
data->mark(s);
}
+SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
+ const SharedInternalClassDataPrivate<PropertyAttributes> &other, uint pos,
+ PropertyAttributes value)
+ : refcount(1),
+ m_alloc(qMin(other.m_alloc, pos + 8)),
+ m_size(pos + 1),
+ m_engine(other.m_engine)
+{
+ Q_ASSERT(m_size <= m_alloc);
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
+ data = new PropertyAttributes[m_alloc];
+ if (other.data)
+ memcpy(data, other.data, (m_size - 1) * sizeof(PropertyAttributes));
+ data[pos] = value;
+}
+
+SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate(
+ const SharedInternalClassDataPrivate<PropertyAttributes> &other)
+ : refcount(1),
+ m_alloc(other.m_alloc),
+ m_size(other.m_size),
+ m_engine(other.m_engine)
+{
+ if (m_alloc) {
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes));
+ data = new PropertyAttributes[m_alloc];
+ memcpy(data, other.data, m_size*sizeof(PropertyAttributes));
+ } else {
+ data = nullptr;
+ }
+}
+
+SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPrivate()
+{
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
+ -qptrdiff(m_alloc * sizeof(PropertyAttributes)));
+ delete [] data;
+}
+void SharedInternalClassDataPrivate<PropertyAttributes>::grow() {
+ uint alloc;
+ if (!m_alloc) {
+ alloc = 8;
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(alloc * sizeof(PropertyAttributes));
+ } else {
+ // yes, signed. We don't want to deal with stuff > 2G
+ const uint currentSize = m_alloc * sizeof(PropertyAttributes);
+ if (currentSize < uint(std::numeric_limits<int>::max() / 2))
+ alloc = m_alloc * 2;
+ else
+ alloc = std::numeric_limits<int>::max() / sizeof(PropertyAttributes);
+
+ m_engine->memoryManager->changeUnmanagedHeapSizeUsage(
+ (alloc - m_alloc) * sizeof(PropertyAttributes));
+ }
+
+ auto *n = new PropertyAttributes[alloc];
+ if (data) {
+ memcpy(n, data, m_alloc*sizeof(PropertyAttributes));
+ delete [] data;
+ }
+ data = n;
+ m_alloc = alloc;
+}
namespace Heap {
@@ -257,11 +321,15 @@ void InternalClass::init(Heap::InternalClass *other)
void InternalClass::destroy()
{
-#ifndef QT_NO_DEBUG
for (const auto &t : transitions) {
- Q_ASSERT(!t.lookup || !t.lookup->isMarked());
- }
+ if (t.lookup) {
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(t.lookup->parent == this);
#endif
+ t.lookup->parent = nullptr;
+ }
+ }
+
if (parent && parent->engine && parent->isMarked())
parent->removeChildEntry(this);
@@ -598,11 +666,10 @@ Heap::InternalClass *InternalClass::frozen()
return f;
}
-Heap::InternalClass *InternalClass::propertiesFrozen() const
+Heap::InternalClass *InternalClass::propertiesFrozen()
{
Scope scope(engine);
- Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
- frozen = frozen->changePrototype(prototype);
+ Scoped<QV4::InternalClass> frozen(scope, this);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (!nameMap.at(i).isValid())
@@ -611,7 +678,7 @@ Heap::InternalClass *InternalClass::propertiesFrozen() const
attrs.setWritable(false);
attrs.setConfigurable(false);
}
- frozen = frozen->addMember(nameMap.at(i), attrs);
+ frozen = frozen->changeMember(nameMap.at(i), attrs);
}
return frozen->d();
}
@@ -659,8 +726,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
if (ic->prototype)
ic->prototype->mark(stack);
- if (ic->parent)
- ic->parent->mark(stack);
ic->nameMap.mark(stack);
}
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index 681cbda5f9..7bb10f47a3 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -149,55 +149,31 @@ inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const
}
}
-template<typename T>
-struct SharedInternalClassDataPrivate {
- SharedInternalClassDataPrivate(ExecutionEngine *)
+template<class T>
+struct SharedInternalClassDataPrivate {};
+
+template<>
+struct SharedInternalClassDataPrivate<PropertyAttributes> {
+ SharedInternalClassDataPrivate(ExecutionEngine *engine)
: refcount(1),
m_alloc(0),
m_size(0),
- data(nullptr)
+ data(nullptr),
+ m_engine(engine)
{ }
- SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other)
- : refcount(1),
- m_alloc(other.m_alloc),
- m_size(other.m_size)
- {
- if (m_alloc) {
- data = new T[m_alloc];
- memcpy(data, other.data, m_size*sizeof(T));
- }
- }
- SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, T value)
- : refcount(1),
- m_alloc(pos + 8),
- m_size(pos + 1)
- {
- data = new T[m_alloc];
- if (other.data)
- memcpy(data, other.data, (m_size - 1)*sizeof(T));
- data[pos] = value;
- }
- ~SharedInternalClassDataPrivate() { delete [] data; }
-
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other);
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other,
+ uint pos, PropertyAttributes value);
+ ~SharedInternalClassDataPrivate();
- void grow() {
- if (!m_alloc)
- m_alloc = 4;
- T *n = new T[m_alloc * 2];
- if (data) {
- memcpy(n, data, m_alloc*sizeof(T));
- delete [] data;
- }
- data = n;
- m_alloc *= 2;
- }
+ void grow();
uint alloc() const { return m_alloc; }
uint size() const { return m_size; }
void setSize(uint s) { m_size = s; }
- T at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
- void set(uint i, T t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
+ PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; }
+ void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; }
void mark(MarkStack *) {}
@@ -205,7 +181,8 @@ struct SharedInternalClassDataPrivate {
private:
uint m_alloc;
uint m_size;
- T *data;
+ PropertyAttributes *data;
+ ExecutionEngine *m_engine;
};
template<>
@@ -270,8 +247,12 @@ struct SharedInternalClassData {
Q_ASSERT(pos == d->size());
if (pos == d->alloc())
d->grow();
- d->setSize(d->size() + 1);
- d->set(pos, value);
+ if (pos >= d->alloc()) {
+ qBadAlloc();
+ } else {
+ d->setSize(d->size() + 1);
+ d->set(pos, value);
+ }
}
void set(uint pos, T value) {
@@ -451,7 +432,7 @@ struct InternalClass : Base {
Q_REQUIRED_RESULT InternalClass *sealed();
Q_REQUIRED_RESULT InternalClass *frozen();
- Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const;
+ Q_REQUIRED_RESULT InternalClass *propertiesFrozen();
Q_REQUIRED_RESULT InternalClass *asProtoClass();
@@ -476,7 +457,7 @@ private:
InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry);
void removeChildEntry(InternalClass *child);
- friend struct ExecutionEngine;
+ friend struct ::QV4::ExecutionEngine;
};
inline
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 1b6cdcbd14..c2c3fa0474 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
- Heap::Object *obj = object->d();
- PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name.isArrayIndex()) {
- indexedLookup.index = name.asArrayIndex();
- getter = getterIndexed;
- return getter(this, engine, *object);
- }
-
- auto index = obj->internalClass->findValueOrGetter(name);
- if (index.isValid()) {
- PropertyAttributes attrs = index.attrs;
- uint nInline = obj->vtable()->nInlineProperties;
- if (attrs.isData()) {
- if (index.index < obj->vtable()->nInlineProperties) {
- index.index += obj->vtable()->inlinePropertyOffset;
- getter = getter0Inline;
- } else {
- index.index -= nInline;
- getter = getter0MemberData;
- }
- } else {
- getter = getterAccessor;
- }
- objectLookup.ic = obj->internalClass;
- objectLookup.offset = index.index;
- return getter(this, engine, *object);
- }
-
- protoLookup.protoId = obj->internalClass->protoId;
- resolveProtoGetter(name, obj->prototype());
- return getter(this, engine, *object);
+ return object->resolveLookupGetter(engine, this);
}
ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
@@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
@@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c
ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
@@ -473,56 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi
bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
{
- Scope scope(engine);
- ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
-
- Heap::InternalClass *c = object->internalClass();
- PropertyKey key = name->toPropertyKey();
- auto idx = c->findValueOrSetter(key);
- if (idx.isValid()) {
- if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
- Q_ASSERT(!idx.attrs.isAccessor());
- setter = arrayLengthSetter;
- return setter(this, engine, *object, value);
- } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
- objectLookup.ic = object->internalClass();
- objectLookup.index = idx.index;
- const auto nInline = object->d()->vtable()->nInlineProperties;
- if (idx.index < nInline) {
- setter = Lookup::setter0Inline;
- objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
- } else {
- setter = Lookup::setter0MemberData;
- objectLookup.offset = idx.index - nInline;
- }
- return setter(this, engine, *object, value);
- } else {
- // ### handle setter
- setter = setterFallback;
- }
- return setter(this, engine, *object, value);
- }
-
- insertionLookup.protoId = c->protoId;
- if (!object->put(key, value)) {
- setter = Lookup::setterFallback;
- return false;
- }
-
- if (object->internalClass() == c) {
- // ### setter in the prototype, should handle this
- setter = setterFallback;
- return true;
- }
- idx = object->internalClass()->findValueOrSetter(key);
- if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
- setter = setterFallback;
- return false;
- }
- insertionLookup.newClass = object->internalClass();
- insertionLookup.offset = idx.index;
- setter = setterInsert;
- return true;
+ return object->resolveLookupSetter(engine, this, value);
}
bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index bfe2354427..7309749a81 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -68,8 +68,10 @@ struct Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
+ ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject);
bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
+ // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null
union {
struct {
Heap::Base *h1;
@@ -119,6 +121,39 @@ struct Lookup {
uint index;
uint unused;
} indexedLookup;
+ struct {
+ Heap::InternalClass *ic;
+ Heap::QObjectWrapper *staticQObject;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qobjectLookup;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr unused;
+ QQmlPropertyCache *propertyCache;
+ QQmlPropertyData *propertyData;
+ } qgadgetLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int scriptIndex;
+ } qmlContextScriptLookup;
+ struct {
+ Heap::Object *singleton;
+ quintptr unused;
+ } qmlContextSingletonLookup;
+ struct {
+ quintptr unused1;
+ quintptr unused2;
+ int objectId;
+ } qmlContextIdObjectLookup;
+ struct {
+ // Same as protoLookup, as used for global lookups
+ quintptr reserved1;
+ quintptr reserved2;
+ quintptr reserved3;
+ ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
+ } qmlContextGlobalLookup;
};
uint nameIndex;
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
index 246f857643..f327c85001 100644
--- a/src/qml/jsruntime/qv4memberdata.cpp
+++ b/src/qml/jsruntime/qv4memberdata.cpp
@@ -69,12 +69,25 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD
size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value));
// round up to next power of two to avoid quadratic behaviour for very large objects
alloc = nextPowerOfTwo(alloc);
- Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc);
- if (old)
+
+ // The above code can overflow in a number of interesting ways. All of those are unsigned,
+ // and therefore defined behavior. Still, apply some sane bounds.
+ if (alloc > std::numeric_limits<int>::max())
+ alloc = std::numeric_limits<int>::max();
+
+ Heap::MemberData *m;
+ if (old) {
+ const size_t oldSize = sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value);
+ if (oldSize > alloc)
+ alloc = oldSize;
+ m = e->memoryManager->allocManaged<MemberData>(alloc);
// no write barrier required here
- memcpy(m, old, sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value));
- else
+ memcpy(m, old, oldSize);
+ } else {
+ m = e->memoryManager->allocManaged<MemberData>(alloc);
m->init();
+ }
+
m->values.alloc = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = m->values.alloc;
return m;
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 3d2d54f651..206b410cf4 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
if (arrayIndex != UINT_MAX && o->arrayData()) {
- if (!arrayIndex)
- arrayNode = o->sparseBegin();
+ SparseArrayNode *arrayNode = nullptr;
+ if (o->arrayType() == Heap::ArrayData::Sparse) {
+ SparseArray *sparse = o->arrayData()->sparse;
+ arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
+ }
// sparse arrays
if (arrayNode) {
@@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr
*attrs = a;
return PropertyKey::fromArrayIndex(k);
}
- arrayNode = nullptr;
arrayIndex = UINT_MAX;
}
// dense arrays
@@ -404,8 +406,8 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h
{
Heap::Object *o = d();
- uint index = id.asArrayIndex();
- if (index != UINT_MAX) {
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
Scope scope(this);
PropertyAttributes attrs;
ScopedProperty pd(scope);
@@ -429,8 +431,6 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h
break;
}
} else {
- Q_ASSERT(!id.isArrayIndex());
-
while (1) {
auto idx = o->internalClass->findValueOrGetter(id);
if (idx.isValid()) {
@@ -468,14 +468,13 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) {
// This object standard methods in the vtable, so we can take a shortcut
// and avoid the calls to getOwnProperty and defineOwnProperty
- uint index = id.asArrayIndex();
PropertyAttributes attrs;
PropertyIndex propertyIndex{nullptr, nullptr};
- if (index != UINT_MAX) {
+ if (id.isArrayIndex()) {
if (arrayData())
- propertyIndex = arrayData()->getValueOrSetter(index, &attrs);
+ propertyIndex = arrayData()->getValueOrSetter(id.asArrayIndex(), &attrs);
} else {
auto member = internalClass()->findValueOrSetter(id);
if (member.isValid()) {
@@ -544,12 +543,11 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) {
// standard object, we can avoid some more checks
- uint index = id.asArrayIndex();
- if (index == UINT_MAX) {
+ if (id.isArrayIndex()) {
+ r->arraySet(id.asArrayIndex(), value);
+ } else {
ScopedStringOrSymbol s(scope, id.asStringOrSymbol());
r->insertMember(s, value);
- } else {
- r->arraySet(index, value);
}
return true;
}
@@ -734,6 +732,95 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v
return checkedInstanceOf(engine, function, var);
}
+ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ Heap::Object *obj = object->d();
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (name.isArrayIndex()) {
+ lookup->indexedLookup.index = name.asArrayIndex();
+ lookup->getter = Lookup::getterIndexed;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ auto index = obj->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
+ uint nInline = obj->vtable()->nInlineProperties;
+ if (attrs.isData()) {
+ if (index.index < obj->vtable()->nInlineProperties) {
+ index.index += obj->vtable()->inlinePropertyOffset;
+ lookup->getter = Lookup::getter0Inline;
+ } else {
+ index.index -= nInline;
+ lookup->getter = Lookup::getter0MemberData;
+ }
+ } else {
+ lookup->getter = Lookup::getterAccessor;
+ }
+ lookup->objectLookup.ic = obj->internalClass;
+ lookup->objectLookup.offset = index.index;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ lookup->protoLookup.protoId = obj->internalClass->protoId;
+ lookup->resolveProtoGetter(name, obj->prototype());
+ return lookup->getter(lookup, engine, *object);
+}
+
+bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ Scope scope(engine);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+
+ Heap::InternalClass *c = object->internalClass();
+ PropertyKey key = name->toPropertyKey();
+ auto idx = c->findValueOrSetter(key);
+ if (idx.isValid()) {
+ if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
+ Q_ASSERT(!idx.attrs.isAccessor());
+ lookup->setter = Lookup::arrayLengthSetter;
+ return lookup->setter(lookup, engine, *object, value);
+ } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
+ lookup->objectLookup.ic = object->internalClass();
+ lookup->objectLookup.index = idx.index;
+ const auto nInline = object->d()->vtable()->nInlineProperties;
+ if (idx.index < nInline) {
+ lookup->setter = Lookup::setter0Inline;
+ lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
+ } else {
+ lookup->setter = Lookup::setter0MemberData;
+ lookup->objectLookup.offset = idx.index - nInline;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ } else {
+ // ### handle setter
+ lookup->setter = Lookup::setterFallback;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ }
+
+ lookup->insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+
+ if (object->internalClass() == c) {
+ // ### setter in the prototype, should handle this
+ lookup->setter = Lookup::setterFallback;
+ return true;
+ }
+ idx = object->internalClass()->findValueOrSetter(key);
+ if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+ lookup->insertionLookup.newClass = object->internalClass();
+ lookup->insertionLookup.offset = idx.index;
+ lookup->setter = Lookup::setterInsert;
+ return true;
+}
+
ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
{
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 72b6703554..567382cbc0 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -375,6 +375,11 @@ public:
bool setProtoFromNewTarget(const Value *newTarget);
+ ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
+ { return vtable()->resolveLookupGetter(this, engine, lookup); }
+ ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
+ { return vtable()->resolveLookupSetter(this, engine, lookup, value); }
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@@ -389,6 +394,8 @@ protected:
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
public:
// qv4runtime uses this directly
static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
@@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
uint arrayIndex = 0;
uint memberIndex = 0;
bool iterateOverSymbols = false;
- SparseArrayNode *arrayNode = nullptr;
~ObjectOwnPropertyKeyIterator() override = default;
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
index 47867765db..523afd4ccf 100644
--- a/src/qml/jsruntime/qv4propertykey_p.h
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -113,8 +113,8 @@ public:
static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; }
static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; }
bool isStringOrSymbol() const { return isManaged() && val != 0; }
- uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); }
- uint isArrayIndex() const { return !isManaged() && val != 0 && static_cast<uint>(val & 0xffffffff) != std::numeric_limits<uint>::max(); }
+ uint asArrayIndex() const { Q_ASSERT(isArrayIndex()); return static_cast<uint>(val & 0xffffffff); }
+ uint isArrayIndex() const { return !isManaged() && val != 0; }
bool isValid() const { return val != 0; }
static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b)
{ PropertyKey key; key.setM(b); return key; }
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 88b0822f42..d2aa334805 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4qmlcontext_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcontext_p.h>
@@ -55,6 +54,8 @@
#include <private/qjsvalue_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@@ -77,14 +78,58 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name,
+ bool *hasProperty, Value *base, QV4::Lookup *lookup,
+ QV4::Lookup *originalLookup, QQmlEnginePrivate *ep)
{
- Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QV4::IdentifierHash &properties = context->propertyNames();
+ if (properties.count() == 0)
+ return OptionalReturnedValue();
+
+ const int propertyIdx = properties.value(name);
+
+ if (propertyIdx == -1)
+ return OptionalReturnedValue();
+
+ if (propertyIdx < context->idValueCount) {
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (lookup) {
+ lookup->qmlContextIdObjectLookup.objectId = propertyIdx;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject;
+ return OptionalReturnedValue(lookup->qmlContextPropertyGetter(lookup, v4, base));
+ } else if (originalLookup) {
+ originalLookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInParentContextHierarchy;
+ }
+
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
+ return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]));
+ }
+
+ QQmlContextPrivate *cp = context->asQQmlContextPrivate();
+
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex);
+
+ const QVariant &value = cp->propertyValues.at(propertyIdx);
+ if (hasProperty)
+ *hasProperty = true;
+ if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
+ QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
+ QQmlContextPrivate::context_count,
+ QQmlContextPrivate::context_at);
+ return OptionalReturnedValue(QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >()));
+ }
+ return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(propertyIdx)));
+}
+ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup)
+{
if (!id.isString())
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
- const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -100,11 +145,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
}
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
}
bool hasProp = false;
- ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
+ ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -160,6 +205,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap
// sub-class as QML type and then instantiates it in .qml.
if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) {
+ // all bets are off, so don't try to optimize any lookups
+ lookup = nullptr;
if (performGobalLookUp())
return result->asReturnedValue();
}
@@ -172,11 +219,35 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProperty)
*hasProperty = true;
if (r.scriptIndex != -1) {
+ if (lookup) {
+ lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} else if (r.type.isValid()) {
+ if (lookup) {
+ if (r.type.isSingleton()) {
+ QQmlEngine *e = v4->qmlEngine();
+ QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo();
+ siinfo->init(e);
+ if (siinfo->qobjectApi(e)) {
+ lookup->qmlContextSingletonLookup.singleton =
+ static_cast<Heap::Object*>(
+ Value::fromReturnedValue(
+ QQmlTypeWrapper::create(v4, nullptr, r.type)
+ ).heapObject());
+ } else {
+ QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
+ lookup->qmlContextSingletonLookup.singleton = o->d();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
+ return lookup->qmlContextPropertyGetter(lookup, v4, base);
+ }
+ }
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
@@ -188,52 +259,47 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
+ Lookup * const originalLookup = lookup;
+
+ decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
+
+ // minor optimization so we don't potentially try two property lookups on the same object
+ if (scopeObject == context->contextObject) {
+ scopeObject = nullptr;
+ contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
while (context) {
- // Search context properties
- const QV4::IdentifierHash &properties = context->propertyNames();
- if (properties.count()) {
- int propertyIdx = properties.value(name);
-
- if (propertyIdx != -1) {
-
- if (propertyIdx < context->idValueCount) {
-
- if (ep->propertyCapture)
- ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
- if (hasProperty)
- *hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]);
- } else {
-
- QQmlContextPrivate *cp = context->asQQmlContextPrivate();
-
- if (ep->propertyCapture)
- ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex);
-
- const QVariant &value = cp->propertyValues.at(propertyIdx);
- if (hasProperty)
- *hasProperty = true;
- if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
- QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx),
- QQmlContextPrivate::context_count,
- QQmlContextPrivate::context_at);
- return QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >());
- } else {
- return scope.engine->fromVariant(cp->propertyValues.at(propertyIdx));
- }
- }
- }
- }
+ if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep))
+ return *property;
// Search scope object
if (scopeObject) {
bool hasProp = false;
+
+ QQmlPropertyData *propertyData = nullptr;
QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
- name, QV4::QObjectWrapper::CheckRevision, &hasProp));
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, scopeObject);
+
+ if (lookup && propertyData) {
+ QQmlData *ddata = QQmlData::get(scopeObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
+ }
+ }
+
return result->asReturnedValue();
}
}
@@ -243,27 +309,77 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search context object
if (context->contextObject) {
bool hasProp = false;
- result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp);
+ QQmlPropertyData *propertyData = nullptr;
+ result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject,
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, context->contextObject);
+
+ if (propertyData) {
+ if (lookup) {
+ QQmlData *ddata = QQmlData::get(context->contextObject, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject)));
+ const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
+ lookup->qobjectLookup.ic = That->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = propertyData;
+ lookup->qmlContextPropertyGetter = contextGetterFunction;
+ }
+ } else if (originalLookup) {
+ originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy;
+ }
+ }
+
return result->asReturnedValue();
}
}
context = context->parent;
+
+ // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
+ // the immediate QML context (of the .qml file).
+ lookup = nullptr;
}
// Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
// true if we access properties of the global object.
- if (performGobalLookUp())
- return result->asReturnedValue();
+ if (originalLookup) {
+ // Try a lookup in the global object. It's theoretically possible to first find a property
+ // in the global object and then later a context property with the same name is added, but that
+ // never really worked as we used to detect access to global properties at type compile time anyway.
+ lookup = originalLookup;
+ result = lookup->resolveGlobalGetter(v4);
+ if (lookup->globalGetter != Lookup::globalGetterGeneric) {
+ if (hasProperty)
+ *hasProperty = true;
+ lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter;
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ return result->asReturnedValue();
+ }
+ lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ } else {
+ if (performGobalLookUp())
+ return result->asReturnedValue();
+ }
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
+ return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr);
+}
+
bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
@@ -298,8 +414,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
// Search context properties
- if (properties.count() && properties.value(name) != -1)
- return false;
+ if (properties.count()) {
+ const int propertyIndex = properties.value(name);
+ if (propertyIndex != -1) {
+ if (propertyIndex < context->idValueCount) {
+ v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue"));
+ return false;
+ }
+ return false;
+ }
+ }
// Search scope object
if (scopeObject &&
@@ -323,6 +447,208 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
return false;
}
+ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+
+ // Special hack for bounded signal expressions, where the parameters of signals are injected
+ // into the handler expression through the locals of the call context. So for onClicked: { ... }
+ // the parameters of the clicked signal are injected and we must allow for them to be found here
+ // before any other property from the QML context.
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) {
+ uint index = ctx.d()->internalClass->indexOfValueOrGetter(name);
+ if (index < UINT_MAX)
+ return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue();
+ }
+
+ bool hasProperty = false;
+ ScopedValue result(scope);
+
+ Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext());
+ if (callingQmlContext) {
+ Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml());
+ result = QQmlContextWrapper::getPropertyAndBase(qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty,
+ base, l);
+ } else {
+ // Code path typical to worker scripts, compiled with lookups but no qml context.
+ result = l->resolveGlobalGetter(engine);
+ if (l->globalGetter != Lookup::globalGetterGeneric) {
+ hasProperty = true;
+ l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ }
+ }
+ if (!hasProperty)
+ return engine->throwReferenceError(name.toQString());
+ return result->asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
+ if (!scripts)
+ return QV4::Encode::null();
+ return scripts->get(l->qmlContextScriptLookup.scriptIndex);
+}
+
+ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(base)
+ return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue();
+}
+
+ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::null();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::null();
+
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine());
+ const int objectId = l->qmlContextIdObjectLookup.objectId;
+
+ if (qmlEngine->propertyCapture)
+ qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings);
+
+ return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]);
+}
+
+ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QObject *scopeObject = qmlContext->qmlScope();
+ if (!scopeObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(scopeObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base)
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::undefined();
+
+ QObject *contextObject = context->contextObject;
+ if (!contextObject)
+ return QV4::Encode::undefined();
+
+ if (QQmlData::wasDeleted(contextObject))
+ return QV4::Encode::undefined();
+
+ const auto revertLookup = [l, engine, base]() {
+ l->qobjectLookup.propertyCache->release();
+ l->qobjectLookup.propertyCache = nullptr;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
+ return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
+ };
+
+ ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
+ return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
+}
+
+ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Q_UNUSED(base);
+ ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine);
+ // In the unlikely event of mutation of the global object, update the trampoline.
+ if (l->qmlContextPropertyGetter != lookupInGlobalObject) {
+ l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
+ l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
+ }
+ return result;
+}
+
+ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
+ if (!qmlContext)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *context = qmlContext->qmlContext();
+ if (!context)
+ return QV4::Encode::undefined();
+
+ QQmlContextData *expressionContext = context;
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
+
+ PropertyKey id =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ ScopedValue result(scope);
+
+ for (context = context->parent; context; context = context->parent) {
+ if (auto property = searchContextProperties(engine, context, name, nullptr, base, nullptr, nullptr, ep))
+ return *property;
+
+ // Search context object
+ if (context->contextObject) {
+ bool hasProp = false;
+ result = QV4::QObjectWrapper::getQmlProperty(engine, context, context->contextObject,
+ name, QV4::QObjectWrapper::CheckRevision, &hasProp);
+ if (hasProp) {
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(engine, context->contextObject);
+
+ return result->asReturnedValue();
+ }
+ }
+ }
+
+ bool hasProp = false;
+ result = engine->globalObject->get(name, &hasProp);
+ if (hasProp)
+ return result->asReturnedValue();
+
+ expressionContext->unresolvedNames = true;
+
+ return Encode::undefined();
+}
+
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index dd6de3323d..4c8287ef2f 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -99,8 +99,19 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
+ static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver,
+ bool *hasProperty, Value *base, Lookup *lookup = nullptr);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+
+ static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
+ static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 15f064ba7a..ba9029bd4d 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -50,12 +50,13 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmlbuiltinfunctions_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4arraybuffer_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
@@ -163,10 +164,6 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object
double v = 0;
property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.isV4Handle()) {
- QQmlV4Handle handle;
- property.readProperty(object, &handle);
- return handle;
} else if (property.propType() == qMetaTypeId<QJSValue>()) {
QJSValue v;
property.readProperty(object, &v);
@@ -231,7 +228,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject
return result;
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
+ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
@@ -257,7 +254,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
+ if (ep && ep->propertyCapture && !property->isConstant())
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
@@ -269,9 +266,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
+static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
+{
+ int index = 0;
+ if (name->equals(v4->id_destroy()))
+ index = QV4::QObjectMethod::DestroyMethod;
+ else if (name->equals(v4->id_toString()))
+ index = QV4::QObjectMethod::ToStringMethod;
+ else
+ return OptionalReturnedValue();
+
+ if (hasProperty)
+ *hasProperty = true;
+ ExecutionContext *global = v4->rootContext();
+ return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index));
+}
+
+static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj,
+ bool *hasProperty = nullptr)
+{
+ if (!qmlContext || !qmlContext->imports)
+ return OptionalReturnedValue();
+
+ QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
+
+ if (hasProperty)
+ *hasProperty = true;
+
+ if (!r.isValid())
+ return OptionalReturnedValue();
+
+ if (r.scriptIndex != -1) {
+ return OptionalReturnedValue(QV4::Encode::undefined());
+ } else if (r.type.isValid()) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
+ } else if (r.importNamespace) {
+ return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace,
+ Heap::QQmlTypeWrapper::ExcludeEnums));
+ }
+ Q_UNREACHABLE();
+ return OptionalReturnedValue();
+}
+
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
bool *hasProperty, bool includeImports) const
{
+ // Keep this code in sync with ::virtualResolveLookupGetter
+
if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
@@ -280,39 +321,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
ExecutionEngine *v4 = engine();
- if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) {
- int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = v4->rootContext();
- return QV4::QObjectMethod::create(global, d()->object(), index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
+ return *methodValue;
QQmlPropertyData local;
QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
if (!result) {
+ // Check for attached properties
if (includeImports && name->startsWithUpper()) {
- // Check for attached properties
- if (qmlContext && qmlContext->imports) {
- QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
-
- if (hasProperty)
- *hasProperty = true;
-
- if (r.isValid()) {
- if (r.scriptIndex != -1) {
- return QV4::Encode::undefined();
- } else if (r.type.isValid()) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- r.type, Heap::QQmlTypeWrapper::ExcludeEnums);
- } else if (r.importNamespace) {
- return QQmlTypeWrapper::create(v4, d()->object(),
- qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums);
- }
- Q_ASSERT(!"Unreachable");
- }
- }
+ if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
+ return *importProperty;
}
return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
@@ -333,27 +352,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
return getProperty(v4, d()->object(), result);
}
-ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired)
-{
- if (QQmlData::wasDeleted(object))
- return QV4::Encode::null();
- QQmlData *ddata = QQmlData::get(object, /*create*/false);
- if (!ddata)
- return QV4::Encode::undefined();
-
- if (Q_UNLIKELY(!ddata->propertyCache)) {
- ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject());
- ddata->propertyCache->addref();
- }
-
- QQmlPropertyCache *cache = ddata->propertyCache;
- Q_ASSERT(cache);
- QQmlPropertyData *property = cache->property(propertyIndex);
- Q_ASSERT(property); // We resolved this property earlier, so it better exist!
- return getProperty(engine, object, property, captureRequired);
-}
-
-ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
+ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@@ -361,13 +360,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
return QV4::Encode::null();
}
- if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) {
- int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
- if (hasProperty)
- *hasProperty = true;
- ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, index);
- }
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
+ return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
QQmlPropertyData local;
@@ -385,6 +379,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
if (hasProperty)
*hasProperty = true;
+ if (property)
+ *property = result;
+
return getProperty(engine, object, result);
} else {
// Check if this object is already wrapped.
@@ -829,6 +826,71 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m,
return new QObjectWrapperOwnPropertyKeyIterator;
}
+ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ // Keep this code in sync with ::getQmlProperty
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ Scope scope(engine);
+
+ const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = engine->callingQmlContext();
+
+ QObject * const qobj = This->d()->object();
+
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
+ return *methodValue;
+
+ QQmlData *ddata = QQmlData::get(qobj, false);
+ if (!ddata || !ddata->propertyCache) {
+ QQmlPropertyData local;
+ QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
+ return getProperty(engine, qobj, property);
+ }
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
+
+ if (!property) {
+ // Check for attached properties
+ if (name->startsWithUpper()) {
+ if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
+ return *importProperty;
+ }
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+ }
+
+ lookup->qobjectLookup.ic = This->internalClass();
+ lookup->qobjectLookup.staticQObject = nullptr;
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qobjectLookup.propertyCache->release();
+ lookup->qobjectLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
+}
+
+bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
@@ -1127,15 +1189,14 @@ DEFINE_OBJECT_VTABLE(QObjectWrapper);
namespace {
-template<typename A, typename B, typename C, typename D, typename E,
- typename F, typename G, typename H>
-class MaxSizeOf8 {
+template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
+class MaxSizeOf7 {
template<typename Z, typename X>
struct SMax {
char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
};
public:
- static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
+ static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, G> > > > > >);
};
struct CallArgument {
@@ -1168,14 +1229,13 @@ private:
std::vector<QUrl> *stdVectorQUrlPtr;
std::vector<QModelIndex> *stdVectorQModelIndexPtr;
- char allocData[MaxSizeOf8<QVariant,
- QString,
- QList<QObject *>,
- QJSValue,
- QQmlV4Handle,
- QJsonArray,
- QJsonObject,
- QJsonValue>::Size];
+ char allocData[MaxSizeOf7<QVariant,
+ QString,
+ QList<QObject *>,
+ QJSValue,
+ QJsonArray,
+ QJsonObject,
+ QJsonValue>::Size];
qint64 q_for_alignment;
};
@@ -1186,7 +1246,6 @@ private:
QVariant *qvariantPtr;
QList<QObject *> *qlistPtr;
QJSValue *qjsValuePtr;
- QQmlV4Handle *handlePtr;
QJsonArray *jsonArrayPtr;
QJsonObject *jsonObjectPtr;
QJsonValue *jsonValuePtr;
@@ -1658,9 +1717,6 @@ void CallArgument::initAsType(int callType)
} else if (callType == qMetaTypeId<QList<QObject *> >()) {
type = callType;
qlistPtr = new (&allocData) QList<QObject *>();
- } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
- type = callType;
- handlePtr = new (&allocData) QQmlV4Handle;
} else if (callType == QMetaType::QJsonArray) {
type = callType;
jsonArrayPtr = new (&allocData) QJsonArray();
@@ -1761,9 +1817,6 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
return false;
}
}
- } else if (callType == qMetaTypeId<QQmlV4Handle>()) {
- handlePtr = new (&allocData) QQmlV4Handle(value.asReturnedValue());
- type = callType;
} else if (callType == QMetaType::QJsonArray) {
QV4::ScopedArrayObject a(scope, value);
jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
@@ -1888,8 +1941,6 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii))));
array->setArrayLengthUnchecked(list.count());
return array.asReturnedValue();
- } else if (type == qMetaTypeId<QQmlV4Handle>()) {
- return *handlePtr;
} else if (type == QMetaType::QJsonArray) {
return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr);
} else if (type == QMetaType::QJsonObject) {
@@ -1923,13 +1974,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
return method.asReturnedValue();
}
-ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
+ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
- method->d()->setPropertyCache(valueType->d()->propertyCache());
+ method->d()->setPropertyCache(valueType->propertyCache());
method->d()->index = index;
- method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
+ method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
return method.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 43a53ac673..795bf241f2 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -60,6 +60,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
+#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -165,7 +166,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const;
- static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr);
+ static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value);
@@ -174,13 +175,18 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
using Object::get;
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired);
static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value);
void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value);
void destroyObject(bool lastCall);
- static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
+ static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
+
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
+ template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+
protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
@@ -216,6 +222,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
return wrap_slowPath(engine, object);
}
+template <typename ReversalFunctor>
+inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
+{
+ // we can safely cast to a QV4::Object here. If object is something else,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (!o || o->internalClass != lookup->qobjectLookup.ic)
+ return revertLookup();
+
+ const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject :
+ static_cast<const Heap::QObjectWrapper *>(o);
+ QObject *qobj = This->object();
+ if (QQmlData::wasDeleted(qobj))
+ return QV4::Encode::undefined();
+
+ QQmlData *ddata = QQmlData::get(qobj, /*create*/false);
+ if (!ddata)
+ return revertLookup();
+
+ QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
+ if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) {
+ if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler()))
+ return revertLookup();
+
+ QQmlPropertyCache *fromMo = ddata->propertyCache;
+ QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache;
+ bool canConvert = false;
+ while (fromMo) {
+ if (fromMo == toMo) {
+ canConvert = true;
+ break;
+ }
+ fromMo = fromMo->parent();
+ }
+ if (!canConvert)
+ return revertLookup();
+ }
+
+ return getProperty(engine, qobj, property);
+}
+
struct QQmlValueTypeWrapper;
struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
@@ -226,7 +273,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
- static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index);
+ static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9753ee4b1d..8f2b162106 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -63,7 +63,6 @@
#include "qv4qobjectwrapper_p.h"
#include "qv4symbol_p.h"
#include "qv4generatorobject_p.h"
-#include <private/qv8engine_p.h>
#endif
#include <QtCore/QDebug>
@@ -1130,6 +1129,12 @@ ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function
return l->globalGetter(l, engine);
}
+ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
+{
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ return l->qmlContextPropertyGetter(l, engine, nullptr);
+}
+
ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
{
Lookup *l = f->compilationUnit->runtimeLookups + index;
@@ -1390,18 +1395,43 @@ uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const
return v->booleanValue();
}
+static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
+{
+ QString objectAsString = QStringLiteral("[null]");
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(propertyName, objectAsString);
+ return engine->throwTypeError(msg);
+}
ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
{
+ Scope scope(engine);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
+ Value thisObject = Value::undefinedValue();
if (!function.isFunctionObject())
- return engine->throwTypeError();
+ return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
- Value thisObject = Value::undefinedValue();
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
+ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index,
+ Value *argv, int argc)
+{
+ Scope scope(engine);
+ ScopedValue thisObject(scope);
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
+ if (!function.isFunctionObject())
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+
+ return static_cast<FunctionObject &>(function).call(thisObject, argv, argc);
+}
+
ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
@@ -1412,13 +1442,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val
if (engine->hasException)
return Encode::undefined();
- if (!function) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!function)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval"));
if (function->d() == engine->evalFunction()->d())
return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
@@ -1437,15 +1462,9 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va
if (engine->hasException)
return Encode::undefined();
- if (!f) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!f)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
return f->call(thisObject, argv, argc);
}
@@ -1482,7 +1501,7 @@ ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
+ .arg(name->toQString(),
base->toQStringNoThrow());
return engine->throwTypeError(error);
}
@@ -1536,41 +1555,6 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val
return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc);
}
-ReturnedValue Runtime::CallQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &base,
- int propertyIndex, Value argv[], int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlScopeObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlScopeObj = static_cast<const QmlContext *>(&base)->d()->qml()->scopeObject;
- ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj));
- return fo->call(qmlScopeValue, argv, argc);
-}
-
-ReturnedValue Runtime::CallQmlContextObjectProperty::call(ExecutionEngine *engine,
- const Value &base,
- int propertyIndex,
- Value argv[],
- int argc)
-{
- Scope scope(engine);
- ScopedFunctionObject fo(scope, LoadQmlContextObjectProperty::call(engine, base, propertyIndex,
- /*captureRequired*/true));
- if (!fo) {
- QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
- return engine->throwTypeError(error);
- }
-
- QObject *qmlContextObj = static_cast<const QmlContext *>(&base)->d()->qml()->context->contextData()->contextObject;
- ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj));
- return fo->call(qmlContextValue, argv, argc);
-}
-
struct CallArgs {
Value *argv;
int argc;
@@ -2013,66 +1997,12 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i
return engine->newArrayObject(values, nValues)->asReturnedValue();
}
-
-ReturnedValue Runtime::LoadQmlContext::call(ExecutionEngine *engine)
-{
- Heap::QmlContext *ctx = engine->qmlContext();
- Q_ASSERT(ctx);
- return ctx->asReturnedValue();
-}
-
ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
{
Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
return ro->asReturnedValue();
}
-ReturnedValue Runtime::LoadQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired);
-}
-
-ReturnedValue Runtime::LoadQmlIdObject::call(ExecutionEngine *engine, const Value &c, uint index)
-{
- const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
- QQmlContextData *context = *qmlContext.d()->qml()->context;
- if (!context || index >= (uint)context->idValueCount)
- return Encode::undefined();
-
- QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
- if (ep && ep->propertyCapture)
- ep->propertyCapture->captureProperty(&context->idValues[index].bindings);
-
- return QObjectWrapper::wrap(engine, context->idValues[index].data());
-}
-
-void Runtime::StoreQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value);
-}
-
-void Runtime::StoreQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
-{
- const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value);
-}
-
-ReturnedValue Runtime::LoadQmlImportedScripts::call(ExecutionEngine *engine)
-{
- QQmlContextData *context = engine->callingQmlContext();
- if (!context)
- return Encode::undefined();
- return context->importedScripts.value();
-}
-
ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
{
if (obj.isObject())
@@ -2155,6 +2085,7 @@ ReturnedValue Runtime::Div::call(const Value &left, const Value &right)
int lval = left.integerValue();
int rval = right.integerValue();
if (rval != 0 // division by zero should result in a NaN
+ && !(lval == std::numeric_limits<int>::min() && rval == -1) // doesn't fit in int
&& (lval % rval == 0) // fractions can't be stored in an int
&& !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
return Encode(int(lval / rval));
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 0312522d90..86cbccde23 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -58,7 +58,6 @@ namespace QV4 {
typedef uint Bool;
-
struct Q_QML_PRIVATE_EXPORT Runtime {
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
@@ -87,6 +86,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
+ struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
+ };
struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, Value[], int);
@@ -187,6 +190,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, Function *, int);
};
+ struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
+ {
+ static ReturnedValue call(ExecutionEngine *, uint);
+ };
struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int);
@@ -495,45 +502,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
static ReturnedValue call(Function *, int);
};
- /* qml */
- struct Q_QML_PRIVATE_EXPORT LoadQmlContext : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlImportedScripts : Method<Throws::No>
- {
- static ReturnedValue call(ExecutionEngine *);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
- };
- struct Q_QML_PRIVATE_EXPORT LoadQmlIdObject : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, uint);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
- struct Q_QML_PRIVATE_EXPORT CallQmlContextObjectProperty : Method<Throws::Yes>
- {
- static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
- };
-
- struct Q_QML_PRIVATE_EXPORT StoreQmlScopeObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
- struct Q_QML_PRIVATE_EXPORT StoreQmlContextObjectProperty : Method<Throws::Yes>
- {
- static void call(ExecutionEngine *, const Value &, int, const Value &);
- };
-
struct StackOffsets {
static const int tailCall_function = -1;
static const int tailCall_thisObject = -2;
diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index be66dc57ca..006a6a3cde 100644
--- a/src/qml/jsruntime/qv4runtimecodegen_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -71,6 +71,7 @@ public:
void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+
private:
ExecutionEngine *engine;
};
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 7bbef3335e..6cb2e95cdc 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -201,7 +201,6 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
}
Codegen cg(unitGenerator, /*strict mode*/false);
- cg.setUseFastLookups(false);
cg.generateFromProgram(fileName, finalUrl, source, program, module, contextType);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp
index 50871a4d87..a84521e205 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qml/jsruntime/qv4serialize.cpp
@@ -39,7 +39,6 @@
#include "qv4serialize_p.h"
-#include <private/qv8engine_p.h>
#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
#include <private/qqmllistmodelworkeragent_p.h>
@@ -375,7 +374,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent));
// ### Find a better solution then the ugly property
QQmlListModelWorkerAgent::VariantRef ref(agent);
- QVariant var = qVariantFromValue(ref);
+ QVariant var = QVariant::fromValue(ref);
QV4::ScopedValue v(scope, scope.engine->fromVariant(var));
QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref")));
rv->as<Object>()->defineReadonlyProperty(s, v);
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 8186153ba4..227df4014e 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope
return PropertyKey::fromArrayIndex(index);
} else if (arrayIndex == slen) {
if (s->arrayData()) {
- arrayNode = s->sparseBegin();
+ SparseArrayNode *arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (arrayNode && arrayNode->key() < slen)
arrayNode = arrayNode->nextNode();
@@ -152,13 +152,14 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert
if (attributes != Attr_Invalid)
return attributes;
- const StringObject *s = static_cast<const StringObject *>(m);
- uint slen = s->d()->string->toQString().length();
- uint index = id.asArrayIndex();
- if (index < slen) {
- if (p)
- p->value = s->getIndex(index);
- return Attr_NotConfigurable|Attr_NotWritable;
+ if (id.isArrayIndex()) {
+ const uint index = id.asArrayIndex();
+ const auto s = static_cast<const StringObject *>(m);
+ if (index < uint(s->d()->string->toQString().length())) {
+ if (p)
+ p->value = s->getIndex(index);
+ return Attr_NotConfigurable|Attr_NotWritable;
+ }
}
return Object::virtualGetOwnProperty(m, id, p);
}
@@ -765,9 +766,8 @@ static void appendReplacementString(QString *result, const QString &input, const
i += skip;
if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch)
*result += input.midRef(substStart, substEnd - substStart);
- else {
+ else if (skip == 0) // invalid capture reference. Taken as literal value
*result += replaceValue.at(i);
- }
} else {
*result += replaceValue.at(i);
}
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index d83f021450..43e1dabb6d 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -459,24 +459,23 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type
ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualGet(m, id, receiver, hasProperty);
- // fall through, with index == UINT_MAX it'll do the right thing.
Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
if (a->d()->buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
- if (index >= a->length()) {
+ if (!isArrayIndex || id.asArrayIndex() >= a->length()) {
if (hasProperty)
*hasProperty = false;
return Encode::undefined();
}
uint bytesPerElement = a->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
+ uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement;
Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
if (hasProperty)
@@ -486,27 +485,22 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val
bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualHasProperty(m, id);
- // fall through, with index == UINT_MAX it'll do the right thing.
const TypedArray *a = static_cast<const TypedArray *>(m);
if (a->d()->buffer->isDetachedBuffer()) {
a->engine()->throwTypeError();
return false;
}
- if (index >= a->length())
- return false;
- return true;
+ return isArrayIndex && id.asArrayIndex() < a->length();
}
PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString())
return Object::virtualGetOwnProperty(m, id, p);
- // fall through, with index == UINT_MAX it'll do the right thing.
bool hasProperty = false;
ReturnedValue v = virtualGet(m, id, m, &hasProperty);
@@ -517,10 +511,9 @@ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyK
bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualPut(m, id, value, receiver);
- // fall through, with index == UINT_MAX it'll do the right thing.
ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
if (v4->hasException)
@@ -531,6 +524,10 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu
if (a->d()->buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
+ if (!isArrayIndex)
+ return false;
+
+ const uint index = id.asArrayIndex();
if (index >= a->length())
return false;
@@ -547,11 +544,12 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu
bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
- return Object::virtualDefineOwnProperty(m, id, p, attrs);
- // fall through, with index == UINT_MAX it'll do the right thing.
+ if (!id.isArrayIndex()) {
+ return !id.isCanonicalNumericIndexString()
+ && Object::virtualDefineOwnProperty(m, id, p, attrs);
+ }
+ const uint index = id.asArrayIndex();
TypedArray *a = static_cast<TypedArray *>(m);
if (index >= a->length() || attrs.isAccessor())
return false;
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index b1cae8796f..da08841026 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -877,6 +877,22 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
+class OptionalReturnedValue {
+ ReturnedValue value;
+public:
+
+ OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {}
+ explicit OptionalReturnedValue(ReturnedValue v)
+ : value(v)
+ {
+ Q_ASSERT(!Value::fromReturnedValue(v).isEmpty());
+ }
+
+ ReturnedValue operator->() const { return value; }
+ ReturnedValue operator*() const { return value; }
+ explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); }
+};
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index e4d8bcaafc..e117e509ab 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -41,7 +41,6 @@
#include "qv4functionobject_p.h"
#include "qv4objectproto_p.h"
#include <private/qqmlvaluetypewrapper_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 3098837e1c..98e4f4f7b9 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -388,6 +388,18 @@ static inline void traceValue(ReturnedValue acc, Function *f, int slot)
#endif
}
+static inline void traceIntValue(Function *f, int slot)
+{
+#if QT_CONFIG(qml_tracing)
+ quint8 *traceInfo = f->traceInfo(slot);
+ Q_ASSERT(traceInfo);
+ *traceInfo |= quint8(ObservedTraceValues::Integer);
+#else
+ Q_UNUSED(f);
+ Q_UNUSED(slot);
+#endif
+}
+
static inline void traceDoubleValue(Function *f, int slot)
{
#if QT_CONFIG(qml_tracing)
@@ -657,6 +669,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ STORE_IP();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->qmlContextPropertyGetter(l, engine, nullptr);
+ CHECK_EXCEPTION;
+ traceValue(acc, function, traceSlot);
+ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(StoreNameStrict)
STORE_IP();
STORE_ACC();
@@ -707,7 +727,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
+
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+
+ if (accumulator.isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(accumulator.toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
@@ -743,37 +773,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreSuperProperty)
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- STORE_ACC();
- Runtime::StoreQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- STORE_IP();
- STORE_ACC();
- Runtime::StoreQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- STORE_IP();
- acc = Runtime::LoadQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- STORE_IP();
- acc = Runtime::LoadQmlIdObject::call(engine, STACK_VALUE(base), index);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadIdObject)
-
MOTH_BEGIN_INSTR(Yield)
frame->yield = code;
frame->yieldIsIterator = false;
@@ -840,11 +839,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+
+ if (stack[base].isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
if (Q_UNLIKELY(!f.isFunctionObject())) {
- acc = engine->throwTypeError();
+ QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
goto handleUnwind;
}
@@ -881,19 +892,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- STORE_IP();
- acc = Runtime::CallQmlScopeObjectProperty::call(engine, stack[base], name, stack + argv, argc);
- CHECK_EXCEPTION;
- traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
- acc = Runtime::CallQmlContextObjectProperty::call(engine, stack[base], name, stack + argv, argc);
+ acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
- MOTH_END_INSTR(CallContextObjectProperty)
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
@@ -1285,7 +1289,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(UNot)
MOTH_BEGIN_INSTR(UPlus)
- if (Q_UNLIKELY(!ACC.isNumber())) {
+ if (Q_LIKELY(ACC.isNumber())) {
+ if (ACC.isDouble())
+ traceDoubleValue(function, traceSlot);
+ else
+ traceIntValue(function, traceSlot);
+ } else {
acc = Encode(ACC.toNumberImpl());
CHECK_EXCEPTION;
}
@@ -1503,14 +1512,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
#endif // QT_CONFIG(qml_debug)
MOTH_END_INSTR(Debug)
- MOTH_BEGIN_INSTR(LoadQmlContext)
- STACK_VALUE(result) = Runtime::LoadQmlContext::call(engine);
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- STACK_VALUE(result) = Runtime::LoadQmlImportedScripts::call(engine);
- MOTH_END_INSTR(LoadQmlImportedScripts)
-
handleUnwind:
Q_ASSERT(engine->hasException || frame->unwindLevel);
if (!frame->unwindHandler) {
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index 00dcb962d3..a4d91640c5 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct Lookup;
+
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
@@ -84,6 +86,9 @@ struct VTable
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
+ typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);
+ typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
+
const VTable * const parent;
quint16 inlinePropertyOffset;
quint16 nInlineProperties;
@@ -118,6 +123,9 @@ struct VTable
Call call;
CallAsConstructor callAsConstructor;
+
+ ResolveLookupGetter resolveLookupGetter;
+ ResolveLookupSetter resolveLookupSetter;
};
@@ -142,6 +150,9 @@ protected:
static constexpr VTable::Call virtualCall = nullptr;
static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+
+ static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr;
+ static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr;
};
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
@@ -181,6 +192,9 @@ protected:
\
classname::virtualCall, \
classname::virtualCallAsConstructor, \
+ \
+ classname::virtualResolveLookupGetter, \
+ classname::virtualResolveLookupSetter \
}
#define DEFINE_MANAGED_VTABLE(classname) \
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 203f1f424f..34d334a24d 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -93,8 +93,6 @@
#include <pthread_np.h>
#endif
-#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024)
-
Q_LOGGING_CATEGORY(lcGcStats, "qt.qml.gc.statistics")
Q_DECLARE_LOGGING_CATEGORY(lcGcStats)
Q_LOGGING_CATEGORY(lcGcAllocatorStats, "qt.qml.gc.allocatorStats")
@@ -759,7 +757,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
, hugeItemAllocator(chunkAllocator, engine)
, m_persistentValues(new PersistentValueStorage(engine))
, m_weakValues(new PersistentValueStorage(engine))
- , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT)
+ , unmanagedHeapSizeGCLimit(MinUnmanagedHeapSizeGCLimit)
, aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
, gcStats(lcGcStats().isDebugEnabled())
, gcCollectorStats(lcGcAllocatorStats().isDebugEnabled())
@@ -779,35 +777,9 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift;
++allocationCount;
#endif
-
- bool didGCRun = false;
- if (aggressiveGC) {
- runGC();
- didGCRun = true;
- }
-
unmanagedHeapSize += unmanagedSize;
- if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
- if (!didGCRun)
- runGC();
-
- if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize)
- // more than 75% full, raise limit
- unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, unmanagedHeapSize) * 2;
- else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit)
- // less than 25% full, lower limit
- unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, unmanagedHeapSizeGCLimit/2);
- didGCRun = true;
- }
-
- HeapItem *m = blockAllocator.allocate(stringSize);
- if (!m) {
- if (!didGCRun && shouldRunGC())
- runGC();
- m = blockAllocator.allocate(stringSize, true);
- }
-// qDebug() << "allocated string" << m;
+ HeapItem *m = allocate(&blockAllocator, stringSize);
memset(m, 0, stringSize);
return *m;
}
@@ -819,32 +791,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size)
++allocationCount;
#endif
- bool didRunGC = false;
- if (aggressiveGC) {
- runGC();
- didRunGC = true;
- }
-
Q_ASSERT(size >= Chunk::SlotSize);
Q_ASSERT(size % Chunk::SlotSize == 0);
-// qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize;
-
- if (size > Chunk::DataSize) {
- HeapItem *h = hugeItemAllocator.allocate(size);
-// qDebug() << "allocating huge item" << h;
- return *h;
- }
-
- HeapItem *m = blockAllocator.allocate(size);
- if (!m) {
- if (!didRunGC && shouldRunGC())
- runGC();
- m = blockAllocator.allocate(size, true);
- }
-
+ HeapItem *m = allocate(&blockAllocator, size);
memset(m, 0, size);
-// qDebug() << "allocating data" << m;
return *m;
}
@@ -1048,12 +999,12 @@ bool MemoryManager::shouldRunGC() const
return false;
}
-size_t dumpBins(BlockAllocator *b, bool printOutput = true)
+static size_t dumpBins(BlockAllocator *b, const char *title)
{
const QLoggingCategory &stats = lcGcAllocatorStats();
size_t totalSlotMem = 0;
- if (printOutput)
- qDebug(stats) << "Slot map:";
+ if (title)
+ qDebug(stats) << "Slot map for" << title << "allocator:";
for (uint i = 0; i < BlockAllocator::NumBins; ++i) {
uint nEntries = 0;
HeapItem *h = b->freeBins[i];
@@ -1062,7 +1013,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true)
totalSlotMem += h->freeData.availableSlots;
h = h->freeData.next;
}
- if (printOutput)
+ if (title)
qDebug(stats) << " number of entries in slot" << i << ":" << nEntries;
}
SDUMP() << " large slot map";
@@ -1072,7 +1023,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true)
h = h->freeData.next;
}
- if (printOutput)
+ if (title)
qDebug(stats) << " total mem in bins" << totalSlotMem*Chunk::SlotSize;
return totalSlotMem*Chunk::SlotSize;
}
@@ -1113,7 +1064,8 @@ void MemoryManager::runGC()
size_t oldChunks = blockAllocator.chunks.size();
qDebug(stats) << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks";
qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore);
- dumpBins(&blockAllocator);
+ dumpBins(&blockAllocator, "Block");
+ dumpBins(&icAllocator, "InternalClass");
QElapsedTimer t;
t.start();
@@ -1131,7 +1083,8 @@ void MemoryManager::runGC()
qDebug(stats) << " new unmanaged heap:" << unmanagedHeapSize;
qDebug(stats) << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit;
}
- size_t memInBins = dumpBins(&blockAllocator);
+ size_t memInBins = dumpBins(&blockAllocator, "Block")
+ + dumpBins(&icAllocator, "InternalClasss");
qDebug(stats) << "Marked object in" << markTime << "us.";
qDebug(stats) << " " << markStackSize << "objects marked";
qDebug(stats) << "Sweeped object in" << sweepTime << "us.";
@@ -1153,7 +1106,8 @@ void MemoryManager::runGC()
qDebug(stats) << "Used memory after GC:" << usedAfter;
qDebug(stats) << "Freed up bytes :" << (usedBefore - usedAfter);
qDebug(stats) << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size());
- size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter;
+ size_t lost = blockAllocator.allocatedMem() + icAllocator.allocatedMem()
+ - memInBins - usedAfter;
if (lost)
qDebug(stats) << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
if (largeItemsBefore || largeItemsAfter) {
@@ -1175,7 +1129,9 @@ void MemoryManager::runGC()
if (aggressiveGC) {
// ensure we don't 'loose' any memory
Q_ASSERT(blockAllocator.allocatedMem()
- == blockAllocator.usedMem() + dumpBins(&blockAllocator, false));
+ == blockAllocator.usedMem() + dumpBins(&blockAllocator, nullptr));
+ Q_ASSERT(icAllocator.allocatedMem()
+ == icAllocator.usedMem() + dumpBins(&icAllocator, nullptr));
}
usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep;
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index bbbbb1aef6..6dfdd81cb2 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -264,13 +264,13 @@ public:
size_t getLargeItemsMem() const;
// called when a JS object grows itself. Specifically: Heap::String::append
+ // and InternalClassDataPrivate<PropertyAttributes>.
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
template<typename ManagedType>
typename ManagedType::Data *allocIC()
{
- size_t size = align(sizeof(typename ManagedType::Data));
- Heap::Base *b = *icAllocator.allocate(size, true);
+ Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data)));
return static_cast<typename ManagedType::Data *>(b);
}
@@ -284,12 +284,52 @@ protected:
Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
private:
+ enum {
+ MinUnmanagedHeapSizeGCLimit = 128 * 1024
+ };
+
void collectFromJSStack(MarkStack *markStack) const;
void mark();
void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
bool shouldRunGC() const;
void collectRoots(MarkStack *markStack);
+ HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
+ {
+ bool didGCRun = false;
+ if (aggressiveGC) {
+ runGC();
+ didGCRun = true;
+ }
+
+ if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
+ if (!didGCRun)
+ runGC();
+
+ if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) {
+ // more than 75% full, raise limit
+ unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit,
+ unmanagedHeapSize) * 2;
+ } else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) {
+ // less than 25% full, lower limit
+ unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit),
+ unmanagedHeapSizeGCLimit/2);
+ }
+ didGCRun = true;
+ }
+
+ if (size > Chunk::DataSize)
+ return hugeItemAllocator.allocate(size);
+
+ if (HeapItem *m = allocator->allocate(size))
+ return m;
+
+ if (!didGCRun && shouldRunGC())
+ runGC();
+
+ return allocator->allocate(size, true);
+ }
+
public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index b86dba6daa..0c947b541b 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -249,7 +249,7 @@
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -486,7 +486,7 @@ protected:
using namespace QQmlJS;
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
void Parser::reallocateStack()
{
@@ -614,16 +614,8 @@ bool Parser::parse(int startToken)
program = 0;
do {
- if (++tos == stack_size) {
+ if (++tos == stack_size)
reallocateStack();
- if (stack_size > 10000) {
- // We're now in some serious right-recursive stuff, which will probably result in
- // an AST that's so deep that recursively visiting it will run out of stack space.
- const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
- return false;
- }
- }
state_stack[tos] = action;
@@ -4484,12 +4476,12 @@ ExportSpecifier: IdentifierName T_AS IdentifierName;
return false;
}
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
./
/:
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 4ebb2d3b5c..e5817ab763 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -41,7 +41,7 @@
#include "qqmljsastvisitor_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -65,21 +65,6 @@ ClassExpression *asAnonymousClassDefinition(Node *n)
return c;
}
-
-void Node::accept(Visitor *visitor)
-{
- if (visitor->preVisit(this)) {
- accept0(visitor);
- }
- visitor->postVisit(this);
-}
-
-void Node::accept(Node *node, Visitor *visitor)
-{
- if (node)
- node->accept(visitor);
-}
-
ExpressionNode *Node::expressionCast()
{
return nullptr;
@@ -1489,6 +1474,6 @@ LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast()
} } // namespace QQmlJS::AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 43aeec6525..b81553776d 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -57,7 +57,7 @@
#include <QtCore/qstring.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
#define QQMLJS_DECLARE_AST_NODE(name) \
enum { K = Kind_##name };
@@ -271,11 +271,29 @@ public:
virtual FunctionExpression *asFunctionDefinition();
virtual ClassExpression *asClassDefinition();
- void accept(Visitor *visitor);
- static void accept(Node *node, Visitor *visitor);
+ inline void accept(Visitor *visitor)
+ {
+ Visitor::RecursionDepthCheck recursionCheck(visitor);
+ if (recursionCheck()) {
+ if (visitor->preVisit(this))
+ accept0(visitor);
+ visitor->postVisit(this);
+ } else {
+ visitor->throwRecursionDepthError();
+ }
+ }
+ inline static void accept(Node *node, Visitor *visitor)
+ {
+ if (node)
+ node->accept(visitor);
+ }
+
+ // ### Remove when we can. This is part of the qmldevtools library, though.
inline static void acceptChild(Node *node, Visitor *visitor)
- { return accept(node, visitor); } // ### remove
+ {
+ return accept(node, visitor);
+ }
virtual void accept0(Visitor *visitor) = 0;
virtual SourceLocation firstSourceLocation() const = 0;
@@ -3377,6 +3395,6 @@ public:
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 7795e0ce71..e9caa918d5 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -56,7 +56,7 @@
// We mean it.
//
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -181,6 +181,6 @@ class UiEnumMemberList;
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp
index eec151298e..5ecac36423 100644
--- a/src/qml/parser/qqmljsastvisitor.cpp
+++ b/src/qml/parser/qqmljsastvisitor.cpp
@@ -39,11 +39,11 @@
#include "qqmljsastvisitor_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
-Visitor::Visitor()
+Visitor::Visitor(quint16 parentRecursionDepth) : m_recursionDepth(parentRecursionDepth)
{
}
@@ -53,4 +53,4 @@ Visitor::~Visitor()
} } // namespace QQmlJS::AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index c925096de6..9115449a46 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -54,14 +54,40 @@
#include "qqmljsastfwd_p.h"
#include "qqmljsglobal_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
class QML_PARSER_EXPORT Visitor
{
public:
- Visitor();
+ class RecursionDepthCheck
+ {
+ Q_DISABLE_COPY(RecursionDepthCheck)
+ public:
+ RecursionDepthCheck(RecursionDepthCheck &&) = delete;
+ RecursionDepthCheck &operator=(RecursionDepthCheck &&) = delete;
+
+ RecursionDepthCheck(Visitor *visitor) : m_visitor(visitor)
+ {
+ ++(m_visitor->m_recursionDepth);
+ }
+
+ ~RecursionDepthCheck()
+ {
+ --(m_visitor->m_recursionDepth);
+ }
+
+ bool operator()() const {
+ return m_visitor->m_recursionDepth < s_recursionLimit;
+ }
+
+ private:
+ static const quint16 s_recursionLimit = 4096;
+ Visitor *m_visitor;
+ };
+
+ Visitor(quint16 parentRecursionDepth = 0);
virtual ~Visitor();
virtual bool preVisit(Node *) { return true; }
@@ -374,10 +400,18 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
+
+ virtual void throwRecursionDepthError() = 0;
+
+ quint16 recursionDepth() const { return m_recursionDepth; }
+
+protected:
+ quint16 m_recursionDepth = 0;
+ friend class RecursionDepthCheck;
};
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSASTVISITOR_P_H
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index 97ce6ebea3..bb27f3992e 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -44,7 +44,7 @@
#include <QtCore/qhash.h>
#include <QtCore/qdebug.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -155,4 +155,4 @@ QStringRef Engine::newStringRef(const QChar *chars, int size)
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index 07b5026eb9..b7f7da9478 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -58,7 +58,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qset.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -150,6 +150,6 @@ double integerFromString(const char *buf, int size, int radix);
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSENGINE_P_H
diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h
index 0e195994b4..bf8155c6ec 100644
--- a/src/qml/parser/qqmljsglobal_p.h
+++ b/src/qml/parser/qqmljsglobal_p.h
@@ -53,8 +53,6 @@
#include <QtCore/qglobal.h>
#ifdef QT_CREATOR
-# define QT_QML_BEGIN_NAMESPACE
-# define QT_QML_END_NAMESPACE
# ifdef QDECLARATIVEJS_BUILD_DIR
# define QML_PARSER_EXPORT Q_DECL_EXPORT
@@ -65,8 +63,6 @@
# endif // QQMLJS_BUILD_DIR
#else // !QT_CREATOR
-# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
-# define QT_QML_END_NAMESPACE QT_END_NAMESPACE
# ifndef QT_STATIC
# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
// QmlDevTools is a static library
diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h
index b0a4951ece..96b3709162 100644
--- a/src/qml/parser/qqmljskeywords_p.h
+++ b/src/qml/parser/qqmljskeywords_p.h
@@ -53,7 +53,7 @@
#include "qqmljslexer_p.h"
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -918,6 +918,6 @@ int Lexer::classify(const QChar *s, int n, int parseModeFlags) {
} // namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // QQMLJSKEYWORDS_P_H
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 03f33f6e06..51152bfd6e 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -57,7 +57,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qstack.h>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -257,6 +257,6 @@ private:
} // end of namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif // LEXER_H
diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h
index bcd6d8672b..e7b1f46414 100644
--- a/src/qml/parser/qqmljsmemorypool_p.h
+++ b/src/qml/parser/qqmljsmemorypool_p.h
@@ -59,7 +59,7 @@
#include <cstring>
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS {
@@ -251,6 +251,6 @@ public:
} // namespace QQmlJS
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/parser/qqmljssourcelocation_p.h b/src/qml/parser/qqmljssourcelocation_p.h
index dc307ba168..d76e701d49 100644
--- a/src/qml/parser/qqmljssourcelocation_p.h
+++ b/src/qml/parser/qqmljssourcelocation_p.h
@@ -55,7 +55,7 @@
// We mean it.
//
-QT_QML_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
@@ -82,6 +82,6 @@ public:
} } // namespace AST
-QT_QML_END_NAMESPACE
+QT_END_NAMESPACE
#endif
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 94717a8f43..d96a1c285a 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -80,6 +80,7 @@ qtConfig(qml-animation) {
include(types/types.pri)
include(../3rdparty/masm/masm-defs.pri)
include(../3rdparty/masm/masm.pri)
+include(../3rdparty/llvm/llvm.pri)
MODULE_PLUGIN_TYPES = \
qmltooling
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 0895e5ae68..9f79bfacdf 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -102,7 +102,6 @@ HEADERS += \
$$PWD/qqmlpropertycachevector_p.h \
$$PWD/qqmlpropertydata_p.h \
$$PWD/qqmlpropertyindex_p.h \
- $$PWD/qqmlpropertyrawdata_p.h \
$$PWD/qqmlmetaobject_p.h \
$$PWD/qqmlnotifier_p.h \
$$PWD/qqmlobjectorgadget_p.h \
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 05a9f70247..3000f56601 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -584,7 +584,6 @@ namespace QtQml {
const QMetaObject *, bool create);
#ifndef Q_QDOC
}
-#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wheader-hygiene")
@@ -594,6 +593,8 @@ using namespace QtQml;
QT_WARNING_POP
+#endif // Q_QDOC
+
//The C++ version of protected namespaces in qmldir
Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion);
Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor);
@@ -605,8 +606,6 @@ QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true)
return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, create);
}
-Q_QML_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor);
-
inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName,
QJSValue (*callback)(QQmlEngine *, QJSEngine *))
{
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index a949df4968..b164517011 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -260,8 +260,6 @@ protected:
} else {
clearError();
}
-
- cancelPermanentGuards();
}
ep->dereferenceScarceResources();
@@ -643,24 +641,22 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
if (!m_target.data())
return dependencies;
- for (const auto &guardList : { permanentGuards, activeGuards }) {
- for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) {
- if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
- continue;
+ for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) {
+ if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
+ continue;
- QObject *senderObject = guard->senderAsObject();
- if (!senderObject)
- continue;
+ QObject *senderObject = guard->senderAsObject();
+ if (!senderObject)
+ continue;
- const QMetaObject *senderMeta = senderObject->metaObject();
- if (!senderMeta)
- continue;
+ const QMetaObject *senderMeta = senderObject->metaObject();
+ if (!senderMeta)
+ continue;
- for (int i = 0; i < senderMeta->propertyCount(); i++) {
- QMetaProperty property = senderMeta->property(i);
- if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
- dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
- }
+ for (int i = 0; i < senderMeta->propertyCount(); i++) {
+ QMetaProperty property = senderMeta->property(i);
+ if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
+ dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
}
}
}
@@ -670,7 +666,7 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
bool QQmlBinding::hasDependencies() const
{
- return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
+ return !activeGuards.isEmpty() || translationsCaptured();
}
class QObjectPointerBinding: public QQmlNonbindingBinding
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index cf6f831818..dc973630a7 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -209,8 +209,6 @@ void QQmlBoundSignalExpression::evaluate(void **a)
} else if (type == QMetaType::Int) {
//### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
jsCall->args[ii] = QV4::Value::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
- } else if (type == qMetaTypeId<QQmlV4Handle>()) {
- jsCall->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]);
} else if (ep->isQObject(type)) {
if (!*reinterpret_cast<void* const *>(a[ii + 1]))
jsCall->args[ii] = QV4::Value::nullValue();
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 57ea685a5d..64d2a064df 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -51,8 +51,6 @@
#include "qqmlincubator_p.h"
#include <private/qqmljavascriptexpression_p.h>
-#include <private/qv8engine_p.h>
-
#include <private/qv4functionobject_p.h>
#include <private/qv4script_p.h>
#include <private/qv4scopedvalue_p.h>
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 61cb0a9065..857b5be8b8 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmldelayedcallqueue_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <private/qv4value_p.h>
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 0a26ed89cc..f070f16afd 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -86,19 +86,10 @@
#if QT_CONFIG(qml_animation)
#include <private/qqmltimer_p.h>
#endif
-#if QT_CONFIG(qml_list_model)
-#include <private/qqmllistmodel_p.h>
-#endif
#include <private/qqmlplatform_p.h>
-#include <private/qquickpackage_p.h>
-#if QT_CONFIG(qml_delegate_model)
-#include <private/qqmldelegatemodel_p.h>
-#endif
-#include <private/qqmlobjectmodel_p.h>
#if QT_CONFIG(qml_worker_script)
#include <private/qquickworkerscript_p.h>
#endif
-#include <private/qqmlinstantiator_p.h>
#include <private/qqmlloggingcategory_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
@@ -116,13 +107,6 @@ Q_DECLARE_METATYPE(QQmlProperty)
QT_BEGIN_NAMESPACE
-void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor)
-{
- QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor);
- QQmlEnginePrivate::registerQtQuick2Types(uri, versionMajor, versionMinor);
- QQmlValueTypeFactory::registerValueTypes(uri, versionMajor, versionMinor);
-}
-
// Declared in qqml.h
int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
const char *uri, int versionMajor,
@@ -215,63 +199,54 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
bool QQmlEnginePrivate::qml_debugging_enabled = false;
bool QQmlEnginePrivate::s_designerMode = false;
-// these types are part of the QML language
-void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int versionMinor)
+void QQmlEnginePrivate::defineModule()
{
- qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component");
- qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject");
- qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding");
- qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8
- qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser);
- if (!strcmp(uri, "QtQuick"))
- qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7
- else
- qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3
+ const char uri[] = "QtQml";
+
+ qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component");
+ qmlRegisterType<QObject>(uri, 2, 0, "QtObject");
+ qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
+ qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding"); // Only available in >= 2.8
+ qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+ qmlRegisterCustomType<QQmlConnections, 1>(uri, 2, 3, "Connections", new QQmlConnectionsParser); // Only available in QtQml >= 2.3
#if QT_CONFIG(qml_animation)
- qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
+ qmlRegisterType<QQmlTimer>(uri, 2, 0, "Timer");
#endif
- qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
- qmlRegisterType<QQmlInstanceModel>();
-
- qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
- qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12
-}
+ qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory"); // Only available in >= 2.8
+ qmlRegisterType<QQmlLoggingCategory, 1>(uri, 2, 12, "LoggingCategory"); // Only available in >= 2.12
-// These QtQuick types' implementation resides in the QtQml module
-void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor)
-{
-#if QT_CONFIG(qml_list_model)
- qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility
- qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility
-#endif
-#if QT_CONFIG(qml_worker_script)
- qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript");
-#endif
- qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package");
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-#if QT_CONFIG(qml_delegate_model)
- qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel");
- qmlRegisterType<QQmlDelegateModelGroup>(uri, versionMajor, versionMinor, "VisualDataGroup");
+#if QT_CONFIG(qml_locale)
+ qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
#endif
- qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel");
-#endif // < Qt 6
}
-void QQmlEnginePrivate::defineQtQuick2Module()
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+void QQmlEnginePrivate::registerQuickTypes()
{
- // register the base types into the QtQuick namespace
- registerBaseTypes("QtQuick",2,0);
+ // Don't add anything here. These are only for backwards compatibility.
+
+ const char uri[] = "QtQuick";
- // register the QtQuick2 types which are implemented in the QtQml module.
- registerQtQuick2Types("QtQuick",2,0);
+ qmlRegisterType<QQmlComponent>(uri, 2, 0, "Component");
+ qmlRegisterType<QObject>(uri, 2, 0, "QtObject");
+ qmlRegisterType<QQmlBind>(uri, 2, 0, "Binding");
+ qmlRegisterType<QQmlBind, 8>(uri, 2, 8, "Binding");
+ qmlRegisterCustomType<QQmlConnections>(uri, 2, 0, "Connections", new QQmlConnectionsParser);
+ qmlRegisterCustomType<QQmlConnections, 1>(uri, 2, 7, "Connections", new QQmlConnectionsParser);
+#if QT_CONFIG(qml_animation)
+ qmlRegisterType<QQmlTimer>(uri, 2, 0,"Timer");
+#endif
+ qmlRegisterType<QQmlLoggingCategory>(uri, 2, 8, "LoggingCategory");
+ qmlRegisterType<QQmlLoggingCategory, 1>(uri, 2, 12, "LoggingCategory");
+#if QT_CONFIG(qml_worker_script)
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+#endif
#if QT_CONFIG(qml_locale)
- qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
+ qmlRegisterUncreatableType<QQmlLocale>(uri, 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
#endif
-
- // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
- qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR);
}
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bool QQmlEnginePrivate::designerMode()
{
@@ -976,14 +951,6 @@ void QQmlEnginePrivate::init()
if (baseModulesUninitialized) {
qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler.
- registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks.
-#if QT_CONFIG(qml_locale)
- qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
-#endif
-
- // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward
- qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR);
-
QQmlData::init();
baseModulesUninitialized = false;
}
@@ -994,7 +961,6 @@ void QQmlEnginePrivate::init()
qRegisterMetaType<QQmlComponent::Status>();
qRegisterMetaType<QList<QObject*> >();
qRegisterMetaType<QList<int> >();
- qRegisterMetaType<QQmlV4Handle>();
qRegisterMetaType<QQmlBinding*>();
v8engine()->setEngine(q);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 20d451d607..dab4e54cd6 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -249,9 +249,10 @@ public:
static QList<QQmlError> qmlErrorFromDiagnostics(const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages);
- static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor);
- static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor);
- static void defineQtQuick2Module();
+ static void defineModule();
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQuickTypes();
+#endif
static bool designerMode();
static void activateDesignerMode();
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index ac2629979f..6a11583f18 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -45,7 +45,6 @@
#include "qqmlcontext_p.h"
#include "qqmlscriptstring_p.h"
#include "qqmlbinding_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlsourcecoordinate_p.h>
#include <QtCore/qdebug.h>
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 1d60c518c4..acebb6bac3 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -149,7 +149,8 @@ QVariant QQmlValueTypeProvider::createVariantFromString(int type, const QString
return QVariant();
}
-QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, QQmlV4Handle obj, QV4::ExecutionEngine *e, bool *ok)
+QVariant QQmlValueTypeProvider::createVariantFromJsObject(int type, const QV4::Value &obj,
+ QV4::ExecutionEngine *e, bool *ok)
{
QVariant v;
@@ -225,63 +226,53 @@ bool QQmlValueTypeProvider::createFromString(int, const QString &, void *, size_
bool QQmlValueTypeProvider::createStringFrom(int, const void *, QString *) { return false; }
bool QQmlValueTypeProvider::variantFromString(const QString &, QVariant *) { return false; }
bool QQmlValueTypeProvider::variantFromString(int, const QString &, QVariant *) { return false; }
-bool QQmlValueTypeProvider::variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *) { return false; }
+bool QQmlValueTypeProvider::variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *) { return false; }
bool QQmlValueTypeProvider::equal(int, const void *, const QVariant&) { return false; }
bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return false; }
bool QQmlValueTypeProvider::read(const QVariant&, void *, int) { return false; }
bool QQmlValueTypeProvider::write(int, const void *, QVariant&) { return false; }
-Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider)
-static QQmlValueTypeProvider *valueTypeProvider = nullptr;
+struct ValueTypeProviderList {
+ QQmlValueTypeProvider nullProvider;
+ QQmlValueTypeProvider *head = &nullProvider;
+};
-static QQmlValueTypeProvider **getValueTypeProvider(void)
-{
- if (valueTypeProvider == nullptr) {
- valueTypeProvider = nullValueTypeProvider;
- }
-
- return &valueTypeProvider;
-}
+Q_GLOBAL_STATIC(ValueTypeProviderList, valueTypeProviders)
Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newProvider)
{
- static QQmlValueTypeProvider **providerPtr = getValueTypeProvider();
- newProvider->next = *providerPtr;
- *providerPtr = newProvider;
+ if (ValueTypeProviderList *providers = valueTypeProviders()) {
+ newProvider->next = providers->head;
+ providers->head = newProvider;
+ }
}
Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider)
{
- if (oldProvider == nullValueTypeProvider) {
- // don't remove the null provider
- // we get here when the QtQml library is being unloaded
- return;
- }
-
- // the only entry with next = 0 is the null provider
- Q_ASSERT(oldProvider->next);
+ if (ValueTypeProviderList *providers = valueTypeProviders()) {
+ QQmlValueTypeProvider *prev = providers->head;
+ if (prev == oldProvider) {
+ providers->head = oldProvider->next;
+ return;
+ }
- QQmlValueTypeProvider *prev = valueTypeProvider;
- if (prev == oldProvider) {
- valueTypeProvider = oldProvider->next;
- return;
- }
+ // singly-linked list removal
+ for (; prev; prev = prev->next) {
+ if (prev->next != oldProvider)
+ continue; // this is not the provider you're looking for
+ prev->next = oldProvider->next;
+ return;
+ }
- // singly-linked list removal
- for ( ; prev; prev = prev->next) {
- if (prev->next != oldProvider)
- continue; // this is not the provider you're looking for
- prev->next = oldProvider->next;
- return;
+ qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider);
}
-
- qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider);
}
-Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void)
+Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider()
{
- static QQmlValueTypeProvider **providerPtr = getValueTypeProvider();
- return *providerPtr;
+ if (ValueTypeProviderList *providers = valueTypeProviders())
+ return providers->head;
+ return nullptr;
}
QQmlColorProvider::~QQmlColorProvider() {}
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index e2d53ab555..53caffe040 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -233,7 +233,7 @@ public:
QVariant createVariantFromString(const QString &);
QVariant createVariantFromString(int, const QString &, bool *);
- QVariant createVariantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, bool*);
+ QVariant createVariantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, bool *);
bool equalValueType(int, const void *, const QVariant&);
bool storeValueType(int, const void *, void *, size_t);
@@ -250,7 +250,7 @@ private:
virtual bool variantFromString(const QString &, QVariant *);
virtual bool variantFromString(int, const QString &, QVariant *);
- virtual bool variantFromJsObject(int, QQmlV4Handle, QV4::ExecutionEngine *, QVariant *);
+ virtual bool variantFromJsObject(int, const QV4::Value &, QV4::ExecutionEngine *, QVariant *);
virtual bool equal(int, const void *, const QVariant&);
virtual bool store(int, const void *, void *, size_t);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 90c2c1fb96..9d5801bbc6 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -141,6 +141,13 @@ struct RegisteredPlugin {
struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> {
QMutex mutex;
+
+ ~StringRegisteredPluginMap()
+ {
+ QMutexLocker lock(&mutex);
+ for (const RegisteredPlugin &plugin : qAsConst(*this))
+ delete plugin.loader;
+ }
};
Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders
@@ -789,7 +796,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
*typeRecursionDetected = true;
} else {
QQmlType returnType = QQmlMetaType::typeForUrl(
- qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, nullptr);
+ qmlUrl, type, registrationType == QQmlType::CompositeSingletonType, errors);
if (type_return)
*type_return = returnType;
return returnType.isValid();
@@ -1256,7 +1263,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ
QString url;
const QStringRef absolutePath = absoluteFilePath.leftRef(absoluteFilePath.lastIndexOf(Slash) + 1);
if (absolutePath.at(0) == Colon)
- url = QLatin1String("qrc://") + absolutePath.mid(1);
+ url = QLatin1String("qrc") + absolutePath;
else
url = QUrl::fromLocalFile(absolutePath.toString()).toString();
@@ -1471,7 +1478,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix
QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
Q_ASSERT(!localFileOrQrc.isEmpty());
- QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
+ const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
if (!typeLoader->directoryExists(dir)) {
if (!isImplicitImport) {
QQmlError error;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 380163202a..9a3a5218e0 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -109,7 +109,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
}
clearActiveGuards();
- clearPermanentGuards();
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = nullptr;
@@ -118,12 +117,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
{
activeGuards.setFlagValue(v);
- permanentGuards.setFlagValue(v);
- if (!v) {
+ if (!v)
clearActiveGuards();
- clearPermanentGuards();
- m_permanentDependenciesRegistered = false;
- }
}
void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
@@ -216,10 +211,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
QV4::Scope scope(v4);
QV4::ScopedValue result(scope, res);
- if (v4Function->hasQmlDependencies) {
- QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d();
- QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction);
- }
if (scope.hasException()) {
if (watcher.wasDeleted())
@@ -254,7 +245,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
return result->asReturnedValue();
}
-void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
+void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
{
if (watcher->wasDeleted())
return;
@@ -274,17 +265,14 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
g->connect(n);
}
- if (duration == Permanently)
- expression->permanentGuards.prepend(g);
- else
- expression->activeGuards.prepend(g);
+ expression->activeGuards.prepend(g);
}
/*! \internal
\a n is in the signal index range (see QObjectPrivate::signalIndex()).
*/
-void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify)
+void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotify)
{
if (watcher->wasDeleted())
return;
@@ -323,61 +311,8 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur
g->connect(o, n, engine, doNotify);
}
- if (duration == Permanently)
- expression->permanentGuards.prepend(g);
- else
- expression->activeGuards.prepend(g);
- }
-}
-
-void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction)
-{
- // Let the caller check and avoid the function call :)
- Q_ASSERT(compiledFunction->hasQmlDependencies());
-
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
- if (!ep)
- return;
- QQmlPropertyCapture *capture = ep->propertyCapture;
- if (!capture || capture->watcher->wasDeleted())
- return;
-
- if (capture->expression->m_permanentDependenciesRegistered)
- return;
-
- capture->expression->m_permanentDependenciesRegistered = true;
-
- QV4::Heap::QQmlContextWrapper *wrapper = context->qml();
- QQmlContextData *qmlContext = wrapper->context->contextData();
-
- const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
- const int idObjectDependencyCount = compiledFunction->nDependingIdObjects;
- for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) {
- Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount);
- capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings,
- QQmlPropertyCapture::Permanently);
- }
-
- Q_ASSERT(qmlContext->contextObject);
- const quint32_le *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable();
- const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties;
- for (int i = 0; i < contextPropertyDependencyCount; ++i) {
- const int propertyIndex = *contextPropertyDependency++;
- const int notifyIndex = *contextPropertyDependency++;
- capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex,
- QQmlPropertyCapture::Permanently);
- }
-
- QObject *scopeObject = wrapper->scopeObject;
- const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
- const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties;
- for (int i = 0; i < scopePropertyDependencyCount; ++i) {
- const int propertyIndex = *scopePropertyDependency++;
- const int notifyIndex = *scopePropertyDependency++;
- capture->captureProperty(scopeObject, propertyIndex, notifyIndex,
- QQmlPropertyCapture::Permanently);
+ expression->activeGuards.prepend(g);
}
-
}
QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
@@ -471,13 +406,6 @@ void QQmlJavaScriptExpression::clearActiveGuards()
g->Delete();
}
-void QQmlJavaScriptExpression::clearPermanentGuards()
-{
- m_permanentDependenciesRegistered = false;
- while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst())
- g->Delete();
-}
-
void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
{
QQmlJavaScriptExpression *expression =
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index de3fba0774..453c8ab8a8 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -144,7 +144,6 @@ public:
QQmlError error(QQmlEngine *) const;
void clearError();
void clearActiveGuards();
- void clearPermanentGuards();
QQmlDelayedError *delayedError();
static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope,
@@ -153,14 +152,6 @@ public:
protected:
void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
- void cancelPermanentGuards() const
- {
- if (m_permanentDependenciesRegistered) {
- for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it))
- it->cancelNotify();
- }
- }
-
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
@@ -169,7 +160,6 @@ protected:
// activeGuards:flag2 - useSharedContext
QBiPointer<QObject, DeleteWatcher> m_scopeObject;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards;
- QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards;
void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); }
bool translationsCaptured() const { return m_error.flag(); }
@@ -186,7 +176,6 @@ private:
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
QQmlJavaScriptExpression *m_nextExpression;
- bool m_permanentDependenciesRegistered = false;
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
@@ -204,14 +193,8 @@ public:
Q_ASSERT(errorString == nullptr);
}
- enum Duration {
- OnlyOnce,
- Permanently
- };
-
- static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction);
- void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce);
- void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true);
+ void captureProperty(QQmlNotifier *);
+ void captureProperty(QObject *, int, int, bool doNotify = true);
void captureTranslation() { translationCaptured = true; }
QQmlEngine *engine;
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 2f769c1aef..5349572921 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmllistwrapper_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmllist_p.h>
#include <private/qv4objectproto_p.h>
#include <qv4objectiterator_p.h>
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 0da96f61e4..32f281b4f2 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -278,6 +278,7 @@ void QQmlMetaType::clearTypeRegistrations()
data->idToType.clear();
data->nameToType.clear();
data->urlToType.clear();
+ data->typePropertyCaches.clear();
data->urlToNonFileImportType.clear();
data->metaObjectToType.clear();
data->uriToModule.clear();
@@ -1206,6 +1207,7 @@ void QQmlMetaType::unregisterType(int typeIndex)
removeQQmlTypePrivate(data->metaObjectToType, d);
for (auto & module : data->uriToModule)
module->remove(d);
+ data->clearPropertyCachesForMinorVersion(typeIndex);
data->types[typeIndex] = QQmlType();
}
}
@@ -1232,6 +1234,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches()
for (auto &module : data->uriToModule)
module->remove(d);
+ data->clearPropertyCachesForMinorVersion(d->index);
*it = QQmlType();
} else {
++it;
diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp
index 13c46e4911..5dc0083f54 100644
--- a/src/qml/qml/qqmlmetatypedata.cpp
+++ b/src/qml/qml/qqmlmetatypedata.cpp
@@ -77,6 +77,27 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
priv->release();
}
+QQmlPropertyCache *QQmlMetaTypeData::propertyCacheForMinorVersion(int index, int minorVersion) const
+{
+ return (index < typePropertyCaches.length())
+ ? typePropertyCaches.at(index).value(minorVersion).data()
+ : nullptr;
+}
+
+void QQmlMetaTypeData::setPropertyCacheForMinorVersion(int index, int minorVersion,
+ QQmlPropertyCache *cache)
+{
+ if (index >= typePropertyCaches.length())
+ typePropertyCaches.resize(index + 1);
+ typePropertyCaches[index][minorVersion] = cache;
+}
+
+void QQmlMetaTypeData::clearPropertyCachesForMinorVersion(int index)
+{
+ if (index < typePropertyCaches.length())
+ typePropertyCaches[index].clear();
+}
+
QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject, int minorVersion)
{
if (QQmlPropertyCache *rv = propertyCaches.value(metaObject))
@@ -97,7 +118,7 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
{
Q_ASSERT(type.isValid());
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion))
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), minorVersion))
return pc;
QVector<QQmlType> types;
@@ -118,8 +139,8 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
metaObject = metaObject->superClass();
}
- if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) {
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc);
+ if (QQmlPropertyCache *pc = propertyCacheForMinorVersion(type.index(), maxMinorVersion)) {
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, pc);
return pc;
}
@@ -190,13 +211,13 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min
}
#endif
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw);
+ setPropertyCacheForMinorVersion(type.index(), minorVersion, raw);
if (hasCopied)
raw->release();
if (minorVersion != maxMinorVersion)
- const_cast<QQmlTypePrivate*>(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw);
+ setPropertyCacheForMinorVersion(type.index(), maxMinorVersion, raw);
return raw;
}
diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h
index 2c5a32be1b..c45bc16280 100644
--- a/src/qml/qml/qqmlmetatypedata_p.h
+++ b/src/qml/qml/qqmlmetatypedata_p.h
@@ -83,6 +83,7 @@ struct QQmlMetaTypeData
MetaObjects metaObjectToType;
typedef QHash<int, QQmlMetaType::StringConverter> StringConverters;
StringConverters stringConverters;
+ QVector<QHash<int, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches;
struct VersionedUri {
VersionedUri()
@@ -112,6 +113,11 @@ struct QQmlMetaTypeData
QHash<int, int> qmlLists;
QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches;
+
+ QQmlPropertyCache *propertyCacheForMinorVersion(int index, int minorVersion) const;
+ void setPropertyCacheForMinorVersion(int index, int minorVersion, QQmlPropertyCache *cache);
+ void clearPropertyCachesForMinorVersion(int index);
+
QQmlPropertyCache *propertyCache(const QMetaObject *metaObject, int minorVersion);
QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion);
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index fc798a2c23..fe0946c6de 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -41,7 +41,6 @@
#include <private/qqmlpropertycache_p.h>
#include <private/qqmldata_p.h>
#include <private/qmetaobjectbuilder_p.h>
-#include <private/qv8engine_p.h>
#include <qqmlengine.h>
#include <qdebug.h>
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 46457a8d76..a6546ae330 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -42,7 +42,6 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlvmemetaobject_p.h>
-#include <private/qv8engine_p.h>
#include <private/qmetaobject_p.h>
#include <private/qmetaobjectbuilder_p.h>
@@ -99,8 +98,6 @@ static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags)
flags.type = QQmlPropertyData::Flags::QmlBindingType;
} else if (propType == qMetaTypeId<QJSValue>()) {
flags.type = QQmlPropertyData::Flags::QJSValueType;
- } else if (propType == qMetaTypeId<QQmlV4Handle>()) {
- flags.type = QQmlPropertyData::Flags::V4HandleType;
} else {
QQmlMetaType::TypeCategory cat = QQmlMetaType::typeCategory(propType);
@@ -127,24 +124,27 @@ QQmlPropertyData::flagsForProperty(const QMetaProperty &p)
return flags;
}
-void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
+static void populate(QQmlPropertyData *data, const QMetaProperty &p)
{
- setCoreIndex(p.propertyIndex());
- setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
Q_ASSERT(p.revision() <= Q_INT16_MAX);
- setRevision(p.revision());
-
- setFlags(fastFlagsForProperty(p));
+ data->setCoreIndex(p.propertyIndex());
+ data->setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
+ data->setFlags(fastFlagsForProperty(p));
+ data->setRevision(p.revision());
+}
+void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
+{
+ populate(this, p);
int type = static_cast<int>(p.type());
if (type == QMetaType::QObjectStar) {
setPropType(type);
- _flags.type = Flags::QObjectDerivedType;
+ m_flags.type = Flags::QObjectDerivedType;
} else if (type == QMetaType::QVariant) {
setPropType(type);
- _flags.type = Flags::QVariantType;
+ m_flags.type = Flags::QVariantType;
} else if (type == QVariant::UserType || type == -1) {
- _flags.notFullyResolved = true;
+ m_flags.notFullyResolved = true;
} else {
setPropType(type);
}
@@ -152,13 +152,9 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
void QQmlPropertyData::load(const QMetaProperty &p)
{
+ populate(this, p);
setPropType(p.userType());
- setCoreIndex(p.propertyIndex());
- setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
- setFlags(fastFlagsForProperty(p));
- flagsForPropertyType(propType(), _flags);
- Q_ASSERT(p.revision() <= Q_INT16_MAX);
- setRevision(p.revision());
+ flagsForPropertyType(propType(), m_flags);
}
void QQmlPropertyData::load(const QMetaMethod &m)
@@ -168,23 +164,23 @@ void QQmlPropertyData::load(const QMetaMethod &m)
setPropType(m.returnType());
- _flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal)
- _flags.isSignal = true;
- else if (m.methodType() == QMetaMethod::Constructor) {
- _flags.isConstructor = true;
+ m_flags.type = Flags::FunctionType;
+ if (m.methodType() == QMetaMethod::Signal) {
+ m_flags.isSignal = true;
+ } else if (m.methodType() == QMetaMethod::Constructor) {
+ m_flags.isConstructor = true;
setPropType(QMetaType::QObjectStar);
}
- if (m.parameterCount()) {
- _flags.hasArguments = true;
- if ((m.parameterCount() == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) {
- _flags.isV4Function = true;
- }
+ const int paramCount = m.parameterCount();
+ if (paramCount) {
+ m_flags.hasArguments = true;
+ if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*"))
+ m_flags.isV4Function = true;
}
if (m.attributes() & QMetaMethod::Cloned)
- _flags.isCloned = true;
+ m_flags.isCloned = true;
Q_ASSERT(m.revision() <= Q_INT16_MAX);
setRevision(m.revision());
@@ -192,37 +188,14 @@ void QQmlPropertyData::load(const QMetaMethod &m)
void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
{
- setCoreIndex(m.methodIndex());
- setPropType(QMetaType::Void);
- setArguments(nullptr);
- _flags.type = Flags::FunctionType;
- if (m.methodType() == QMetaMethod::Signal)
- _flags.isSignal = true;
- else if (m.methodType() == QMetaMethod::Constructor) {
- _flags.isConstructor = true;
- setPropType(QMetaType::QObjectStar);
- }
+ load(m);
const char *returnType = m.typeName();
if (!returnType)
returnType = "\0";
if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
- _flags.notFullyResolved = true;
- }
-
- const int paramCount = m.parameterCount();
- if (paramCount) {
- _flags.hasArguments = true;
- if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) {
- _flags.isV4Function = true;
- }
+ m_flags.notFullyResolved = true;
}
-
- if (m.attributes() & QMetaMethod::Cloned)
- _flags.isCloned = true;
-
- Q_ASSERT(m.revision() <= Q_INT16_MAX);
- setRevision(m.revision());
}
/*!
@@ -347,7 +320,7 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag
data.setArguments(nullptr);
QQmlPropertyData handler = data;
- handler._flags.isSignalHandler = true;
+ handler.m_flags.isSignalHandler = true;
if (types) {
int argumentCount = *types;
@@ -545,7 +518,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->setFlags(methodFlags);
data->lazyLoad(m);
- data->_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -553,7 +526,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (data->isSignal()) {
sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
*sigdata = *data;
- sigdata->_flags.isSignalHandler = true;
+ sigdata->m_flags.isSignalHandler = true;
}
QQmlPropertyData *old = nullptr;
@@ -595,7 +568,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (old) {
// We only overload methods in the same class, exactly like C++
if (old->isFunction() && old->coreIndex() >= methodOffset)
- data->_flags.isOverload = true;
+ data->m_flags.isOverload = true;
data->markAsOverrideOf(old);
}
@@ -626,7 +599,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
data->lazyLoad(p);
data->setTypeMinorVersion(typeMinorVersion);
- data->_flags.isDirect = !dynamicMetaObject;
+ data->m_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
@@ -652,7 +625,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
}
if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
- data->_flags.isDirect = false;
+ data->m_flags.isDirect = false;
else
data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
if (old)
@@ -663,7 +636,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
{
Q_ASSERT(data->notFullyResolved());
- data->_flags.notFullyResolved = false;
+ data->m_flags.notFullyResolved = false;
const QMetaObject *mo = firstCppMetaObject();
if (data->isFunction()) {
@@ -698,7 +671,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult);
}
}
- flagsForPropertyType(data->propType(), data->_flags);
+ flagsForPropertyType(data->propType(), data->m_flags);
}
}
@@ -875,7 +848,7 @@ void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
setOverrideIndexIsProperty(!predecessor->isFunction());
setOverrideIndex(predecessor->coreIndex());
- predecessor->_flags.isOverridden = true;
+ predecessor->m_flags.isOverridden = true;
}
QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names)
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 4f47e5d351..72692ee522 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -89,19 +89,19 @@ public:
QQmlPropertyCache *copy();
QQmlPropertyCache *copyAndAppend(const QMetaObject *,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndReserve(int propertyCount,
int methodCount, int signalCount, int enumCount);
- void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex,
+ void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
int propType, int revision, int notifyIndex);
- void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex,
+ void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex,
const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>());
void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
const QList<QByteArray> &names = QList<QByteArray>());
@@ -187,9 +187,9 @@ private:
inline QQmlPropertyCache *copy(int reserve);
void append(const QMetaObject *, int typeMinorVersion,
- QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(),
- QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
- QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
+ QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names);
@@ -243,8 +243,6 @@ private:
QByteArray _checksum;
};
-typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr;
-
inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
if (p && Q_UNLIKELY(p->notFullyResolved()))
diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h
index a292bcc769..dec696226e 100644
--- a/src/qml/qml/qqmlpropertydata_p.h
+++ b/src/qml/qml/qqmlpropertydata_p.h
@@ -51,11 +51,12 @@
// We mean it.
//
-#include <private/qqmlpropertyrawdata_p.h>
+#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
-class QQmlPropertyData : public QQmlPropertyRawData
+class QQmlPropertyCacheMethodArguments;
+class QQmlPropertyData
{
public:
enum WriteFlag {
@@ -65,10 +66,193 @@ public:
};
Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
- inline QQmlPropertyData();
- inline QQmlPropertyData(const QQmlPropertyRawData &);
+ typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
- inline bool operator==(const QQmlPropertyRawData &);
+ struct Flags {
+ enum Types {
+ OtherType = 0,
+ FunctionType = 1, // Is an invokable
+ QObjectDerivedType = 2, // Property type is a QObject* derived type
+ EnumType = 3, // Property type is an enum
+ QListType = 4, // Property type is a QML list
+ QmlBindingType = 5, // Property type is a QQmlBinding*
+ QJSValueType = 6, // Property type is a QScriptValue
+ // Gap, used to be V4HandleType
+ VarPropertyType = 8, // Property type is a "var" property of VMEMO
+ QVariantType = 9 // Property is a QVariant
+ };
+
+ // The _otherBits (which "pad" the Flags struct to align it nicely) are used
+ // to store the relative property index. It will only get used when said index fits. See
+ // trySetStaticMetaCallFunction for details.
+ // (Note: this padding is done here, because certain compilers have surprising behavior
+ // when an enum is declared in-between two bit fields.)
+ enum { BitsLeftInFlags = 10 };
+ unsigned otherBits : BitsLeftInFlags; // align to 32 bits
+
+ // Can apply to all properties, except IsFunction
+ unsigned isConstant : 1; // Has CONST flag
+ unsigned isWritable : 1; // Has WRITE function
+ unsigned isResettable : 1; // Has RESET function
+ unsigned isAlias : 1; // Is a QML alias to another property
+ unsigned isFinal : 1; // Has FINAL flag
+ unsigned isOverridden : 1; // Is overridden by a extension property
+ unsigned isDirect : 1; // Exists on a C++ QMetaObject
+
+ unsigned type : 4; // stores an entry of Types
+
+ // Apply only to IsFunctions
+ unsigned isVMEFunction : 1; // Function was added by QML
+ unsigned hasArguments : 1; // Function takes arguments
+ unsigned isSignal : 1; // Function is a signal
+ unsigned isVMESignal : 1; // Signal was added by QML
+ unsigned isV4Function : 1; // Function takes QQmlV4Function* args
+ unsigned isSignalHandler : 1; // Function is a signal handler
+ unsigned isOverload : 1; // Function is an overload of another function
+ unsigned isCloned : 1; // The function was marked as cloned
+ unsigned isConstructor : 1; // The function was marked is a constructor
+
+ // Internal QQmlPropertyCache flags
+ unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
+ unsigned overrideIndexIsProperty: 1;
+
+ inline Flags();
+ inline bool operator==(const Flags &other) const;
+ inline void copyPropertyTypeFlags(Flags from);
+ };
+
+ inline bool operator==(const QQmlPropertyData &) const;
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f)
+ {
+ unsigned otherBits = m_flags.otherBits;
+ m_flags = f;
+ m_flags.otherBits = otherBits;
+ }
+
+ bool isValid() const { return coreIndex() != -1; }
+
+ bool isConstant() const { return m_flags.isConstant; }
+ bool isWritable() const { return m_flags.isWritable; }
+ void setWritable(bool onoff) { m_flags.isWritable = onoff; }
+ bool isResettable() const { return m_flags.isResettable; }
+ bool isAlias() const { return m_flags.isAlias; }
+ bool isFinal() const { return m_flags.isFinal; }
+ bool isOverridden() const { return m_flags.isOverridden; }
+ bool isDirect() const { return m_flags.isDirect; }
+ bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
+ bool isFunction() const { return m_flags.type == Flags::FunctionType; }
+ bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; }
+ bool isEnum() const { return m_flags.type == Flags::EnumType; }
+ bool isQList() const { return m_flags.type == Flags::QListType; }
+ bool isQmlBinding() const { return m_flags.type == Flags::QmlBindingType; }
+ bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; }
+ bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; }
+ bool isQVariant() const { return m_flags.type == Flags::QVariantType; }
+ bool isVMEFunction() const { return m_flags.isVMEFunction; }
+ bool hasArguments() const { return m_flags.hasArguments; }
+ bool isSignal() const { return m_flags.isSignal; }
+ bool isVMESignal() const { return m_flags.isVMESignal; }
+ bool isV4Function() const { return m_flags.isV4Function; }
+ bool isSignalHandler() const { return m_flags.isSignalHandler; }
+ bool isOverload() const { return m_flags.isOverload; }
+ void setOverload(bool onoff) { m_flags.isOverload = onoff; }
+ bool isCloned() const { return m_flags.isCloned; }
+ bool isConstructor() const { return m_flags.isConstructor; }
+
+ bool hasOverride() const { return overrideIndex() >= 0; }
+ bool hasRevision() const { return revision() != 0; }
+
+ bool isFullyResolved() const { return !m_flags.notFullyResolved; }
+
+ int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; }
+ void setPropType(int pt)
+ {
+ Q_ASSERT(pt >= 0);
+ Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
+ m_propType = quint16(pt);
+ }
+
+ int notifyIndex() const { return m_notifyIndex; }
+ void setNotifyIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_notifyIndex = qint16(idx);
+ }
+
+ bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; }
+ void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; }
+
+ int overrideIndex() const { return m_overrideIndex; }
+ void setOverrideIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_overrideIndex = qint16(idx);
+ }
+
+ int coreIndex() const { return m_coreIndex; }
+ void setCoreIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ m_coreIndex = qint16(idx);
+ }
+
+ quint8 revision() const { return m_revision; }
+ void setRevision(quint8 rev)
+ {
+ Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
+ m_revision = quint8(rev);
+ }
+
+ /* If a property is a C++ type, then we store the minor
+ * version of this type.
+ * This is required to resolve property or signal revisions
+ * if this property is used as a grouped property.
+ *
+ * Test.qml
+ * property TextEdit someTextEdit: TextEdit {}
+ *
+ * Test {
+ * someTextEdit.preeditText: "test" //revision 7
+ * someTextEdit.onEditingFinished: console.log("test") //revision 6
+ * }
+ *
+ * To determine if these properties with revisions are available we need
+ * the minor version of TextEdit as imported in Test.qml.
+ *
+ */
+
+ quint8 typeMinorVersion() const { return m_typeMinorVersion; }
+ void setTypeMinorVersion(quint8 rev)
+ {
+ Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
+ m_typeMinorVersion = quint8(rev);
+ }
+
+ QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; }
+ void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; }
+
+ int metaObjectOffset() const { return m_metaObjectOffset; }
+ void setMetaObjectOffset(int off)
+ {
+ Q_ASSERT(off >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(off <= std::numeric_limits<qint16>::max());
+ m_metaObjectOffset = qint16(off);
+ }
+
+ StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; }
+ void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
+ {
+ if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
+ m_flags.otherBits = relativePropertyIndex;
+ m_staticMetaCallFunction = f;
+ }
+ }
+ quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; }
static Flags flagsForProperty(const QMetaProperty &);
void load(const QMetaProperty &);
@@ -128,27 +312,32 @@ private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return _flags.notFullyResolved; }
-};
+ bool notFullyResolved() const { return m_flags.notFullyResolved; }
-QQmlPropertyData::QQmlPropertyData()
-{
- setCoreIndex(-1);
- setPropType(0);
- setNotifyIndex(-1);
- setOverrideIndex(-1);
- setRevision(0);
- setMetaObjectOffset(-1);
- setArguments(nullptr);
- trySetStaticMetaCallFunction(nullptr, 0);
-}
+ Flags m_flags;
+ qint16 m_coreIndex = -1;
+ quint16 m_propType = 0;
-QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
-{
- *(static_cast<QQmlPropertyRawData *>(this)) = d;
-}
+ // The notify index is in the range returned by QObjectPrivate::signalIndex().
+ // This is different from QMetaMethod::methodIndex().
+ qint16 m_notifyIndex = -1;
+ qint16 m_overrideIndex = -1;
-bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
+ quint8 m_revision = 0;
+ quint8 m_typeMinorVersion = 0;
+ qint16 m_metaObjectOffset = -1;
+
+ QQmlPropertyCacheMethodArguments *m_arguments = nullptr;
+ StaticMetaCallFunction m_staticMetaCallFunction = nullptr;
+};
+
+#if QT_POINTER_SIZE == 4
+ Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24);
+#else // QT_POINTER_SIZE == 8
+ Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32);
+#endif
+
+bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const
{
return flags() == other.flags() &&
propType() == other.propType() &&
@@ -157,6 +346,64 @@ bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
revision() == other.revision();
}
+QQmlPropertyData::Flags::Flags()
+ : otherBits(0)
+ , isConstant(false)
+ , isWritable(false)
+ , isResettable(false)
+ , isAlias(false)
+ , isFinal(false)
+ , isOverridden(false)
+ , isDirect(false)
+ , type(OtherType)
+ , isVMEFunction(false)
+ , hasArguments(false)
+ , isSignal(false)
+ , isVMESignal(false)
+ , isV4Function(false)
+ , isSignalHandler(false)
+ , isOverload(false)
+ , isCloned(false)
+ , isConstructor(false)
+ , notFullyResolved(false)
+ , overrideIndexIsProperty(false)
+{}
+
+bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const
+{
+ return isConstant == other.isConstant &&
+ isWritable == other.isWritable &&
+ isResettable == other.isResettable &&
+ isAlias == other.isAlias &&
+ isFinal == other.isFinal &&
+ isOverridden == other.isOverridden &&
+ type == other.type &&
+ isVMEFunction == other.isVMEFunction &&
+ hasArguments == other.hasArguments &&
+ isSignal == other.isSignal &&
+ isVMESignal == other.isVMESignal &&
+ isV4Function == other.isV4Function &&
+ isSignalHandler == other.isSignalHandler &&
+ isOverload == other.isOverload &&
+ isCloned == other.isCloned &&
+ isConstructor == other.isConstructor &&
+ notFullyResolved == other.notFullyResolved &&
+ overrideIndexIsProperty == other.overrideIndexIsProperty;
+}
+
+void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from)
+{
+ switch (from.type) {
+ case QObjectDerivedType:
+ case EnumType:
+ case QListType:
+ case QmlBindingType:
+ case QJSValueType:
+ case QVariantType:
+ type = from.type;
+ }
+}
+
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags)
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertyrawdata_p.h b/src/qml/qml/qqmlpropertyrawdata_p.h
deleted file mode 100644
index 833c0f6ad0..0000000000
--- a/src/qml/qml/qqmlpropertyrawdata_p.h
+++ /dev/null
@@ -1,341 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QQMLPROPERTYRAWDATA_P_H
-#define QQMLPROPERTYRAWDATA_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qobject_p.h>
-
-QT_BEGIN_NAMESPACE
-
-// We have this somewhat awful split between RawData and Data so that RawData can be
-// used in unions. In normal code, you should always use Data which initializes RawData
-// to an invalid state on construction.
-// ### We should be able to remove this split nowadays
-class QQmlPropertyCacheMethodArguments;
-class QQmlPropertyRawData
-{
-public:
- typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
-
- struct Flags {
- enum Types {
- OtherType = 0,
- FunctionType = 1, // Is an invokable
- QObjectDerivedType = 2, // Property type is a QObject* derived type
- EnumType = 3, // Property type is an enum
- QListType = 4, // Property type is a QML list
- QmlBindingType = 5, // Property type is a QQmlBinding*
- QJSValueType = 6, // Property type is a QScriptValue
- V4HandleType = 7, // Property type is a QQmlV4Handle
- VarPropertyType = 8, // Property type is a "var" property of VMEMO
- QVariantType = 9 // Property is a QVariant
- };
-
- // The _otherBits (which "pad" the Flags struct to align it nicely) are used
- // to store the relative property index. It will only get used when said index fits. See
- // trySetStaticMetaCallFunction for details.
- // (Note: this padding is done here, because certain compilers have surprising behavior
- // when an enum is declared in-between two bit fields.)
- enum { BitsLeftInFlags = 10 };
- unsigned _otherBits : BitsLeftInFlags; // align to 32 bits
-
- // Can apply to all properties, except IsFunction
- unsigned isConstant : 1; // Has CONST flag
- unsigned isWritable : 1; // Has WRITE function
- unsigned isResettable : 1; // Has RESET function
- unsigned isAlias : 1; // Is a QML alias to another property
- unsigned isFinal : 1; // Has FINAL flag
- unsigned isOverridden : 1; // Is overridden by a extension property
- unsigned isDirect : 1; // Exists on a C++ QMetaObject
-
- unsigned type : 4; // stores an entry of Types
-
- // Apply only to IsFunctions
- unsigned isVMEFunction : 1; // Function was added by QML
- unsigned hasArguments : 1; // Function takes arguments
- unsigned isSignal : 1; // Function is a signal
- unsigned isVMESignal : 1; // Signal was added by QML
- unsigned isV4Function : 1; // Function takes QQmlV4Function* args
- unsigned isSignalHandler : 1; // Function is a signal handler
- unsigned isOverload : 1; // Function is an overload of another function
- unsigned isCloned : 1; // The function was marked as cloned
- unsigned isConstructor : 1; // The function was marked is a constructor
-
- // Internal QQmlPropertyCache flags
- unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
- unsigned overrideIndexIsProperty: 1;
-
- inline Flags();
- inline bool operator==(const Flags &other) const;
- inline void copyPropertyTypeFlags(Flags from);
- };
-
- Flags flags() const { return _flags; }
- void setFlags(Flags f)
- {
- unsigned otherBits = _flags._otherBits;
- _flags = f;
- _flags._otherBits = otherBits;
- }
-
- bool isValid() const { return coreIndex() != -1; }
-
- bool isConstant() const { return _flags.isConstant; }
- bool isWritable() const { return _flags.isWritable; }
- void setWritable(bool onoff) { _flags.isWritable = onoff; }
- bool isResettable() const { return _flags.isResettable; }
- bool isAlias() const { return _flags.isAlias; }
- bool isFinal() const { return _flags.isFinal; }
- bool isOverridden() const { return _flags.isOverridden; }
- bool isDirect() const { return _flags.isDirect; }
- bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
- bool isFunction() const { return _flags.type == Flags::FunctionType; }
- bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; }
- bool isEnum() const { return _flags.type == Flags::EnumType; }
- bool isQList() const { return _flags.type == Flags::QListType; }
- bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; }
- bool isQJSValue() const { return _flags.type == Flags::QJSValueType; }
- bool isV4Handle() const { return _flags.type == Flags::V4HandleType; }
- bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; }
- bool isQVariant() const { return _flags.type == Flags::QVariantType; }
- bool isVMEFunction() const { return _flags.isVMEFunction; }
- bool hasArguments() const { return _flags.hasArguments; }
- bool isSignal() const { return _flags.isSignal; }
- bool isVMESignal() const { return _flags.isVMESignal; }
- bool isV4Function() const { return _flags.isV4Function; }
- bool isSignalHandler() const { return _flags.isSignalHandler; }
- bool isOverload() const { return _flags.isOverload; }
- void setOverload(bool onoff) { _flags.isOverload = onoff; }
- bool isCloned() const { return _flags.isCloned; }
- bool isConstructor() const { return _flags.isConstructor; }
-
- bool hasOverride() const { return overrideIndex() >= 0; }
- bool hasRevision() const { return revision() != 0; }
-
- bool isFullyResolved() const { return !_flags.notFullyResolved; }
-
- int propType() const { Q_ASSERT(isFullyResolved()); return _propType; }
- void setPropType(int pt)
- {
- Q_ASSERT(pt >= 0);
- Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
- _propType = quint16(pt);
- }
-
- int notifyIndex() const { return _notifyIndex; }
- void setNotifyIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _notifyIndex = qint16(idx);
- }
-
- bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; }
- void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; }
-
- int overrideIndex() const { return _overrideIndex; }
- void setOverrideIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _overrideIndex = qint16(idx);
- }
-
- int coreIndex() const { return _coreIndex; }
- void setCoreIndex(int idx)
- {
- Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
- Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
- _coreIndex = qint16(idx);
- }
-
- quint8 revision() const { return _revision; }
- void setRevision(quint8 rev)
- {
- Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
- _revision = quint8(rev);
- }
-
- /* If a property is a C++ type, then we store the minor
- * version of this type.
- * This is required to resolve property or signal revisions
- * if this property is used as a grouped property.
- *
- * Test.qml
- * property TextEdit someTextEdit: TextEdit {}
- *
- * Test {
- * someTextEdit.preeditText: "test" //revision 7
- * someTextEdit.onEditingFinished: console.log("test") //revision 6
- * }
- *
- * To determine if these properties with revisions are available we need
- * the minor version of TextEdit as imported in Test.qml.
- *
- */
-
- quint8 typeMinorVersion() const { return _typeMinorVersion; }
- void setTypeMinorVersion(quint8 rev)
- {
- Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
- _typeMinorVersion = quint8(rev);
- }
-
- QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; }
- void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; }
-
- int metaObjectOffset() const { return _metaObjectOffset; }
- void setMetaObjectOffset(int off)
- {
- Q_ASSERT(off >= std::numeric_limits<qint16>::min());
- Q_ASSERT(off <= std::numeric_limits<qint16>::max());
- _metaObjectOffset = qint16(off);
- }
-
- StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; }
- void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
- {
- if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
- _flags._otherBits = relativePropertyIndex;
- _staticMetaCallFunction = f;
- }
- }
- quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; }
-
-private:
- Flags _flags;
- qint16 _coreIndex = 0;
- quint16 _propType = 0;
-
- // The notify index is in the range returned by QObjectPrivate::signalIndex().
- // This is different from QMetaMethod::methodIndex().
- qint16 _notifyIndex = 0;
- qint16 _overrideIndex = 0;
-
- quint8 _revision = 0;
- quint8 _typeMinorVersion = 0;
- qint16 _metaObjectOffset = 0;
-
- QQmlPropertyCacheMethodArguments *_arguments = nullptr;
- StaticMetaCallFunction _staticMetaCallFunction = nullptr;
-
- friend class QQmlPropertyData;
- friend class QQmlPropertyCache;
-};
-
-#if QT_POINTER_SIZE == 4
- Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24);
-#else // QT_POINTER_SIZE == 8
- Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32);
-#endif
-
-QQmlPropertyRawData::Flags::Flags()
- : _otherBits(0)
- , isConstant(false)
- , isWritable(false)
- , isResettable(false)
- , isAlias(false)
- , isFinal(false)
- , isOverridden(false)
- , isDirect(false)
- , type(OtherType)
- , isVMEFunction(false)
- , hasArguments(false)
- , isSignal(false)
- , isVMESignal(false)
- , isV4Function(false)
- , isSignalHandler(false)
- , isOverload(false)
- , isCloned(false)
- , isConstructor(false)
- , notFullyResolved(false)
- , overrideIndexIsProperty(false)
-{}
-
-bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const
-{
- return isConstant == other.isConstant &&
- isWritable == other.isWritable &&
- isResettable == other.isResettable &&
- isAlias == other.isAlias &&
- isFinal == other.isFinal &&
- isOverridden == other.isOverridden &&
- type == other.type &&
- isVMEFunction == other.isVMEFunction &&
- hasArguments == other.hasArguments &&
- isSignal == other.isSignal &&
- isVMESignal == other.isVMESignal &&
- isV4Function == other.isV4Function &&
- isSignalHandler == other.isSignalHandler &&
- isOverload == other.isOverload &&
- isCloned == other.isCloned &&
- isConstructor == other.isConstructor &&
- notFullyResolved == other.notFullyResolved &&
- overrideIndexIsProperty == other.overrideIndexIsProperty;
-}
-
-void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from)
-{
- switch (from.type) {
- case QObjectDerivedType:
- case EnumType:
- case QListType:
- case QmlBindingType:
- case QJSValueType:
- case V4HandleType:
- case QVariantType:
- type = from.type;
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QQMLPROPERTYRAWDATA_P_H
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index efe190cbcf..88eedec061 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -118,7 +118,8 @@ QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const
QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
: regType(type), iid(nullptr), typeId(0), listId(0), revision(0),
containsRevisionedAttributes(false), baseMetaObject(nullptr),
- index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false)
+ index(-1), isSetup(false), isEnumFromCacheSetup(false), isEnumFromBaseSetup(false),
+ haveSuperType(false)
{
switch (type) {
case QQmlType::CppType:
@@ -176,8 +177,8 @@ QQmlTypePrivate::~QQmlTypePrivate()
}
QQmlType::QQmlType() = default;
-QQmlType::QQmlType(const QQmlType &other) = default;
-QQmlType::QQmlType(QQmlType &&other) = default;
+QQmlType::QQmlType(const QQmlType &) = default;
+QQmlType::QQmlType(QQmlType &&) = default;
QQmlType &QQmlType::operator =(const QQmlType &other) = default;
QQmlType &QQmlType::operator =(QQmlType &&other) = default;
QQmlType::QQmlType(const QQmlTypePrivate *priv) : d(priv) {}
@@ -347,19 +348,24 @@ void QQmlTypePrivate::init() const
void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const
{
- if (isEnumSetup) return;
+ if ((isEnumFromBaseSetup || !baseMetaObject)
+ && (isEnumFromCacheSetup || !cache)) {
+ return;
+ }
init();
QMutexLocker lock(QQmlMetaType::typeRegistrationLock());
- if (isEnumSetup) return;
- if (cache)
+ if (!isEnumFromCacheSetup && cache) {
insertEnumsFromPropertyCache(cache);
- if (baseMetaObject) // could be singleton type without metaobject
- insertEnums(baseMetaObject);
+ isEnumFromCacheSetup = true;
+ }
- isEnumSetup = true;
+ if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject
+ insertEnums(baseMetaObject);
+ isEnumFromBaseSetup = true;
+ }
}
void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
@@ -503,25 +509,6 @@ void QQmlTypePrivate::setName(const QString &uri, const QString &element)
name = uri.isEmpty() ? element : (uri + QLatin1Char('/') + element);
}
-QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersion) const
-{
- for (int i = 0; i < propertyCaches.count(); ++i)
- if (propertyCaches.at(i).minorVersion == minorVersion)
- return propertyCaches.at(i).cache.data();
- return nullptr;
-}
-
-void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache)
-{
- for (int i = 0; i < propertyCaches.count(); ++i) {
- if (propertyCaches.at(i).minorVersion == minorVersion) {
- propertyCaches[i].cache = cache;
- return;
- }
- }
- propertyCaches.append(PropertyCacheByMinorVersion(cache, minorVersion));
-}
-
QByteArray QQmlType::typeName() const
{
if (d) {
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index 1d1345a0cb..0e59b1be06 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -89,7 +89,6 @@ public:
}
bool isValid() const { return !d.isNull(); }
- const QQmlTypePrivate *key() const { return d.data(); }
QByteArray typeName() const;
QString qmlTypeName() const;
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 380139f385..b317aff740 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -120,24 +120,14 @@ public:
int index;
mutable volatile bool isSetup:1;
- mutable volatile bool isEnumSetup:1;
+ mutable volatile bool isEnumFromCacheSetup:1;
+ mutable volatile bool isEnumFromBaseSetup:1;
mutable bool haveSuperType:1;
mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
mutable QStringHash<int> enums;
mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
mutable QList<QStringHash<int>*> scopedEnums;
- struct PropertyCacheByMinorVersion
- {
- PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {}
- explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {}
- QQmlPropertyCachePtr cache;
- int minorVersion;
- };
- QVector<PropertyCacheByMinorVersion> propertyCaches;
- QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const;
- void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache);
-
void setName(const QString &uri, const QString &element);
private:
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 3ec828ea2d..86513b5ff8 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qqmltypewrapper_p.h"
-#include <private/qv8engine_p.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcontext_p.h>
@@ -48,6 +47,8 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@@ -170,6 +171,7 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n
ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ // Keep this code in sync with ::virtualResolveLookupGetter
Q_ASSERT(m->as<QQmlTypeWrapper>());
if (!id.isString())
@@ -426,6 +428,64 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType));
}
+ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ // Keep this code in sync with ::virtualGet
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+ Scope scope(engine);
+
+ const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object);
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = engine->callingQmlContext();
+
+ Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This));
+ QQmlType type = w->d()->type();
+
+ if (type.isValid()) {
+
+ if (type.isSingleton()) {
+ QQmlEngine *e = engine->qmlEngine();
+ QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
+ siinfo->init(e);
+
+ QObject *qobjectSingleton = siinfo->qobjectApi(e);
+ if (qobjectSingleton) {
+
+ const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
+ if (!includeEnums || !name->startsWithUpper()) {
+ QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
+ if (ddata && ddata->propertyCache) {
+ ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
+ QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
+ if (property) {
+ lookup->qobjectLookup.ic = This->internalClass();
+ lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject());
+ lookup->qobjectLookup.propertyCache = ddata->propertyCache;
+ lookup->qobjectLookup.propertyCache->addref();
+ lookup->qobjectLookup.propertyData = property;
+ lookup->getter = QV4::QObjectWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *This);
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ // Fall through to base implementation
+ }
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+}
+
+bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
void Heap::QQmlScopedEnumWrapper::destroy()
{
QQmlType::derefHandle(typePrivate);
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index bc615e0f6c..c797a4ac10 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -111,6 +111,9 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 21505754bb..f08005fd20 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -80,19 +80,26 @@ QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl()
bool QQmlValueTypeFactoryImpl::isValueType(int idx)
{
- if (idx >= (int)QVariant::UserType) {
- return (valueType(idx) != nullptr);
- } else if (idx >= 0
- && idx != QVariant::StringList
- && idx != QMetaType::QObjectStar
- && idx != QMetaType::VoidStar
- && idx != QMetaType::Nullptr
- && idx != QMetaType::QVariant
- && idx != QMetaType::QLocale) {
+ if (idx >= QMetaType::User)
+ return valueType(idx) != nullptr;
+
+ if (idx < 0)
+ return false;
+
+ // Qt internal types
+ switch (idx) {
+ case QMetaType::QStringList:
+ case QMetaType::QObjectStar:
+ case QMetaType::VoidStar:
+ case QMetaType::Nullptr:
+ case QMetaType::QVariant:
+ case QMetaType::QLocale:
+ case QMetaType::QImage: // scarce type, keep as QVariant
+ case QMetaType::QPixmap: // scarce type, keep as QVariant
+ return false;
+ default:
return true;
}
-
- return false;
}
const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t)
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 9ce1c82f09..cf6553d129 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qqmlvaluetypewrapper_p.h"
-#include <private/qv8engine_p.h>
+
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlglobal_p.h>
@@ -51,6 +51,8 @@
#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
@@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
+Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper,
+ QQmlPropertyData *property)
+{
+ if (property->isFunction()) {
+ // calling a Q_INVOKABLE function of a value type
+ return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex());
+ }
+
+#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
+ if (property->propType() == metatype) { \
+ cpptype v; \
+ void *args[] = { &v, nullptr }; \
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \
+ QMetaObject::ReadProperty, index, args); \
+ return QV4::Encode(constructor(v)); \
+ }
+
+ const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject();
+
+ int index = property->coreIndex();
+ QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
+
+ // These four types are the most common used by the value type wrappers
+ VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
+ VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int);
+ VALUE_TYPE_LOAD(QMetaType::Int, int, int);
+ VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
+ VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
+
+ QVariant v;
+ void *args[] = { nullptr, nullptr };
+ if (property->propType() == QMetaType::QVariant) {
+ args[0] = &v;
+ } else {
+ v = QVariant(property->propType(), static_cast<void *>(nullptr));
+ args[0] = v.data();
+ }
+ metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty,
+ index, args);
+ return engine->fromVariant(v);
+#undef VALUE_TYPE_LOAD
+}
+
+ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
+ Lookup *lookup)
+{
+ PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[lookup->nameIndex]);
+ if (!id.isString())
+ return Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
+ QV4::ExecutionEngine *v4 = r->engine();
+ Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ // Note: readReferenceValue() can change the reference->type.
+ if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
+ if (!reference->readReferenceValue())
+ return Value::undefinedValue().asReturnedValue();
+ }
+
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
+ if (!result)
+ return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
+
+ lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.propertyCache = r->d()->propertyCache();
+ lookup->qgadgetLookup.propertyCache->addref();
+ lookup->qgadgetLookup.propertyData = result;
+ lookup->getter = QQmlValueTypeWrapper::lookupGetter;
+ return lookup->getter(lookup, engine, *object);
+}
+
+ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
+{
+ const auto revertLookup = [lookup, engine, &object]() {
+ lookup->qgadgetLookup.propertyCache->release();
+ lookup->qgadgetLookup.propertyCache = nullptr;
+ lookup->getter = Lookup::getterGeneric;
+ return Lookup::getterGeneric(lookup, engine, object);
+ };
+
+ // we can safely cast to a QV4::Object here. If object is something else,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (!o || o->internalClass != lookup->qgadgetLookup.ic)
+ return revertLookup();
+
+ Heap::QQmlValueTypeWrapper *valueTypeWrapper =
+ const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
+ if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache)
+ return revertLookup();
+
+ if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) {
+ Scope scope(engine);
+ Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper);
+ referenceWrapper->readReferenceValue();
+ }
+
+ QQmlPropertyData *property = lookup->qgadgetLookup.propertyData;
+ return getGadgetProperty(engine, valueTypeWrapper, property);
+}
+
+bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
+ const Value &value)
+{
+ return Object::virtualResolveLookupSetter(object, engine, lookup, value);
+}
+
ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
@@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
if (hasProperty)
*hasProperty = true;
- if (result->isFunction())
- // calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex());
-
-#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (result->propType() == metatype) { \
- cpptype v; \
- void *args[] = { &v, 0 }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \
- return QV4::Encode(constructor(v)); \
- }
-
- const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
-
- int index = result->coreIndex();
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
-
- void *gadget = r->d()->gadgetPtr;
-
- // These four types are the most common used by the value type wrappers
- VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
- VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int);
- VALUE_TYPE_LOAD(QMetaType::Int, int, int);
- VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString);
- VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
-
- QVariant v;
- void *args[] = { nullptr, nullptr };
- if (result->propType() == QMetaType::QVariant) {
- args[0] = &v;
- } else {
- v = QVariant(result->propType(), static_cast<void *>(nullptr));
- args[0] = v.data();
- }
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args);
- return v4->fromVariant(v);
-#undef VALUE_TYPE_ACCESSOR
+ return getGadgetProperty(v4, r->d(), result);
}
bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index 8db9474132..baac129afa 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -112,6 +112,9 @@ public:
static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
+ static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object);
static void initProto(ExecutionEngine *v4);
};
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 6bc469c836..5d13415513 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -594,7 +594,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const
QV4::Scope scope(engine);
QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) {
- QVariant variant(qVariantFromValue(QList<QObject*>()));
+ QVariant variant(QVariant::fromValue(QList<QObject*>()));
v = engine->newVariantObject(variant);
md->set(engine, id, v);
}
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 87dc9fb0b2..2371d70f10 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -66,7 +66,6 @@
#include "qqmlguard_p.h"
#include "qqmlcontext_p.h"
-#include <private/qv8engine_p.h>
#include <private/qflagpointer_p.h>
#include <private/qv4object_p.h>
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 9f629f974d..c6b7f2ab3f 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -39,8 +39,6 @@
#include "qqmlxmlhttprequest_p.h"
-#include <private/qv8engine_p.h>
-
#include "qqmlengine.h"
#include "qqmlengine_p.h"
#include <private/qqmlrefcount_p.h>
@@ -70,8 +68,6 @@
using namespace QV4;
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
-
#define V4THROW_REFERENCE(string) \
do { \
ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string))); \
@@ -2067,6 +2063,4 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
QT_END_NAMESPACE
-#endif // xmlstreamreader && qml_network
-
#include <qqmlxmlhttprequest.moc>
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 64dc581a56..3bc588b50e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -436,7 +436,7 @@ ReturnedValue QtObject::method_font(const FunctionObject *b, const Value *, cons
QV4::ExecutionEngine *v4 = scope.engine;
bool ok = false;
- QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, QQmlV4Handle(argv[0]), v4, &ok);
+ QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QFont, argv[0], v4, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.font(): Invalid argument: no valid font subproperties specified");
return scope.engine->fromVariant(v);
@@ -540,7 +540,7 @@ ReturnedValue QtObject::method_matrix4x4(const FunctionObject *b, const Value *,
if (argc == 1 && argv[0].isObject()) {
bool ok = false;
- QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, QQmlV4Handle(argv[0]), scope.engine, &ok);
+ QVariant v = QQml_valueTypeProvider()->createVariantFromJsObject(QMetaType::QMatrix4x4, argv[0], scope.engine, &ok);
if (!ok)
THROW_GENERIC_ERROR("Qt.matrix4x4(): Invalid argument: not a valid matrix4x4 values array");
return scope.engine->fromVariant(v);
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index 23559618ef..f9b69cfacc 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -132,19 +132,6 @@ private:
QV4::ExecutionEngine *e;
};
-class Q_QML_PRIVATE_EXPORT QQmlV4Handle
-{
-public:
- QQmlV4Handle() : d(QV4::Encode::undefined()) {}
- explicit QQmlV4Handle(const QV4::Value &v) : d(v.asReturnedValue()) {}
- explicit QQmlV4Handle(QV4::ReturnedValue v) : d(v) {}
-
- operator QV4::ReturnedValue() const { return d; }
-
-private:
- quint64 d;
-};
-
class QObject;
class QQmlEngine;
class QNetworkAccessManager;
@@ -238,6 +225,4 @@ inline QV8Engine::Deletable *QV8Engine::extensionData(int index) const
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QQmlV4Handle)
-
#endif // QQMLV8ENGINE_P_H
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 60988d12e6..46f0e3f409 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -57,7 +57,7 @@
# include <QtQml/private/qtqml-config_p.h>
#endif
-#define Q_QML_PRIVATE_API_VERSION 3
+#define Q_QML_PRIVATE_API_VERSION 4
#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 572f58339f..0e57119368 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -1127,7 +1127,7 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
return QQmlIncubator::Ready;
}
-QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
+QVariant QQmlDelegateModelPrivate::variantValue(QQmlListCompositor::Group group, int index, const QString &name)
{
Compositor::iterator it = m_compositor.find(group, index);
if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) {
@@ -1139,20 +1139,20 @@ QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index
while (dot > 0) {
QObject *obj = qvariant_cast<QObject*>(value);
if (!obj)
- return QString();
- int from = dot+1;
+ return QVariant();
+ const int from = dot + 1;
dot = name.indexOf(QLatin1Char('.'), from);
value = obj->property(name.midRef(from, dot - from).toUtf8());
}
- return value.toString();
+ return value;
}
- return QString();
+ return QVariant();
}
-QString QQmlDelegateModel::stringValue(int index, const QString &name)
+QVariant QQmlDelegateModel::variantValue(int index, const QString &role)
{
Q_D(QQmlDelegateModel);
- return d->stringValue(d->m_compositorGroup, index, name);
+ return d->variantValue(d->m_compositorGroup, index, role);
}
int QQmlDelegateModel::indexOf(QObject *item, QObject *) const
@@ -2419,17 +2419,15 @@ void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::G
bool QQmlDelegateModelGroupPrivate::isChangedConnected()
{
Q_Q(QQmlDelegateModelGroup);
- IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &));
+ IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QJSValue &,const QJSValue &));
}
void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4)
{
Q_Q(QQmlDelegateModelGroup);
if (isChangedConnected() && !changeSet.isEmpty()) {
- QV4::Scope scope(v4);
- QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes()));
- QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts()));
- emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted));
+ emit q->changed(QJSValue(v4, engineData(v4)->array(v4, changeSet.removes())),
+ QJSValue(v4, engineData(v4)->array(v4, changeSet.inserts())));
}
if (changeSet.difference() != 0)
emit q->countChanged();
@@ -2607,18 +2605,18 @@ void QQmlDelegateModelGroup::setDefaultInclude(bool include)
\endlist
*/
-QQmlV4Handle QQmlDelegateModelGroup::get(int index)
+QJSValue QQmlDelegateModelGroup::get(int index)
{
Q_D(QQmlDelegateModelGroup);
if (!d->model)
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
if (!model->m_context || !model->m_context->isValid()) {
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
} else if (index < 0 || index >= model->m_compositor.count(d->group)) {
qmlWarning(this) << tr("get: index out of range");
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
}
Compositor::iterator it = model->m_compositor.find(d->group, index);
@@ -2630,7 +2628,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
cacheItem = model->m_adaptorModel.createItem(
model->m_cacheMetaType, it.modelIndex());
if (!cacheItem)
- return QQmlV4Handle(QV4::Encode::undefined());
+ return QJSValue();
cacheItem->groups = it->flags;
model->m_cache.insert(it.cacheIndex, cacheItem);
@@ -2646,7 +2644,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
o->setPrototypeOf(p);
++cacheItem->scriptRef;
- return QQmlV4Handle(o);
+ return QJSValue(v4, o->asReturnedValue());
}
bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const
@@ -3338,9 +3336,9 @@ QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item)
return flags;
}
-QString QQmlPartsModel::stringValue(int index, const QString &role)
+QVariant QQmlPartsModel::variantValue(int index, const QString &role)
{
- return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role);
+ return QQmlDelegateModelPrivate::get(m_model)->variantValue(m_compositorGroup, index, role);
}
void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles)
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
index 0ad8939732..2684162514 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -59,7 +59,6 @@
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qstringlist.h>
-#include <private/qv8engine_p.h>
#include <private/qqmlglobal_p.h>
QT_REQUIRE_CONFIG(qml_delegate_model);
@@ -114,7 +113,7 @@ public:
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *object) override;
void cancel(int index) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
void setWatchedRoles(const QList<QByteArray> &roles) override;
QQmlIncubator::Status incubationStatus(int index) override;
@@ -179,7 +178,7 @@ public:
bool defaultInclude() const;
void setDefaultInclude(bool include);
- Q_INVOKABLE QQmlV4Handle get(int index);
+ Q_INVOKABLE QJSValue get(int index);
public Q_SLOTS:
void insert(QQmlV4Function *);
@@ -195,7 +194,7 @@ Q_SIGNALS:
void countChanged();
void nameChanged();
void defaultIncludeChanged();
- void changed(const QQmlV4Handle &removed, const QQmlV4Handle &inserted);
+ void changed(const QJSValue &removed, const QJSValue &inserted);
private:
Q_DECLARE_PRIVATE(QQmlDelegateModelGroup)
};
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 0028849828..7f10bbf370 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -276,7 +276,7 @@ public:
void requestMoreIfNecessary();
QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode);
QQmlDelegateModel::ReleaseFlags release(QObject *object);
- QString stringValue(Compositor::Group group, int index, const QString &name);
+ QVariant variantValue(Compositor::Group group, int index, const QString &name);
void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) {
@@ -378,7 +378,7 @@ public:
bool isValid() const override;
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *item) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
QList<QByteArray> watchedRoles() const { return m_watchedRoles; }
void setWatchedRoles(const QList<QByteArray> &roles) override;
QQmlIncubator::Status incubationStatus(int index) override;
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 27171b9bd4..5b5bcd8464 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -52,6 +52,7 @@
#include <private/qv4dateobject_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4lookup_p.h>
#include <qqmlcontext.h>
#include <qqmlinfo.h>
@@ -1604,8 +1605,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
if (ep && ep->propertyCapture)
- ep->propertyCapture->captureProperty(that->object(), -1, role->index,
- QQmlPropertyCapture::OnlyOnce, false);
+ ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false);
}
const int elementIndex = that->d()->elementIndex();
@@ -1613,6 +1613,12 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
return that->engine()->fromVariant(value);
}
+ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ lookup->getter = Lookup::getterFallback;
+ return lookup->getter(lookup, engine, *object);
+}
+
struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
int roleNameIndex = 0;
@@ -2539,7 +2545,7 @@ void QQmlListModel::append(QQmlV4Function *args)
\sa append()
*/
-QQmlV4Handle QQmlListModel::get(int index) const
+QJSValue QQmlListModel::get(int index) const
{
QV4::Scope scope(engine());
QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
@@ -2562,7 +2568,7 @@ QQmlV4Handle QQmlListModel::get(int index) const
}
}
- return QQmlV4Handle(result);
+ return QJSValue(engine(), result->asReturnedValue());
}
/*!
@@ -2581,10 +2587,10 @@ QQmlV4Handle QQmlListModel::get(int index) const
\sa append()
*/
-void QQmlListModel::set(int index, const QQmlV4Handle &handle)
+void QQmlListModel::set(int index, const QJSValue &value)
{
QV4::Scope scope(engine());
- QV4::ScopedObject object(scope, handle);
+ QV4::ScopedObject object(scope, QJSValuePrivate::getValue(&value));
if (!object) {
qmlWarning(this) << tr("set: value is not an object");
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index 95b797c898..471e33aa5a 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -100,8 +100,8 @@ public:
Q_INVOKABLE void remove(QQmlV4Function *args);
Q_INVOKABLE void append(QQmlV4Function *args);
Q_INVOKABLE void insert(QQmlV4Function *args);
- Q_INVOKABLE QQmlV4Handle get(int index) const;
- Q_INVOKABLE void set(int index, const QQmlV4Handle &);
+ Q_INVOKABLE QJSValue get(int index) const;
+ Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
Q_INVOKABLE void move(int from, int to, int count);
Q_INVOKABLE void sync();
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index ff52ee049f..2876c71de6 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -181,6 +181,8 @@ struct ModelObject : public QObjectWrapper
protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp
index fe3eaa3198..f7cb08dcf4 100644
--- a/src/qml/types/qqmllistmodelworkeragent.cpp
+++ b/src/qml/types/qqmllistmodelworkeragent.cpp
@@ -114,12 +114,12 @@ void QQmlListModelWorkerAgent::insert(QQmlV4Function *args)
m_copy->insert(args);
}
-QQmlV4Handle QQmlListModelWorkerAgent::get(int index) const
+QJSValue QQmlListModelWorkerAgent::get(int index) const
{
return m_copy->get(index);
}
-void QQmlListModelWorkerAgent::set(int index, const QQmlV4Handle &value)
+void QQmlListModelWorkerAgent::set(int index, const QJSValue &value)
{
m_copy->set(index, value);
}
diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h
index ae2d4b11e0..69d1785618 100644
--- a/src/qml/types/qqmllistmodelworkeragent_p.h
+++ b/src/qml/types/qqmllistmodelworkeragent_p.h
@@ -85,8 +85,8 @@ public:
Q_INVOKABLE void remove(QQmlV4Function *args);
Q_INVOKABLE void append(QQmlV4Function *args);
Q_INVOKABLE void insert(QQmlV4Function *args);
- Q_INVOKABLE QQmlV4Handle get(int index) const;
- Q_INVOKABLE void set(int index, const QQmlV4Handle &);
+ Q_INVOKABLE QJSValue get(int index) const;
+ Q_INVOKABLE void set(int index, const QJSValue &value);
Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
Q_INVOKABLE void move(int from, int to, int count);
Q_INVOKABLE void sync();
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index b7b9c9ee1c..840b435d18 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -48,9 +48,43 @@
#endif
#include <private/qqmlobjectmodel_p.h>
#include <private/qqmltablemodel_p.h>
+#include <private/qqmltablemodelcolumn_p.h>
+#include <private/qqmlinstantiator_p.h>
+#include <private/qquickpackage_p.h>
QT_BEGIN_NAMESPACE
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlModelsModule::registerQmlTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+ qmlRegisterType<QQmlInstantiator>("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1
+ qmlRegisterType<QQmlInstanceModel>();
+}
+
+void QQmlModelsModule::registerQuickTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+
+ const char uri[] = "QtQuick";
+
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 1, "Instantiator");
+ qmlRegisterType<QQmlInstanceModel>();
+#if QT_CONFIG(qml_list_model)
+ qmlRegisterType<QQmlListElement>(uri, 2, 0, "ListElement");
+ qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser);
+#endif
+ qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package");
+#if QT_CONFIG(qml_delegate_model)
+ qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel");
+ qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup");
+#endif
+ qmlRegisterType<QQmlObjectModel>(uri, 2, 0, "VisualItemModel");
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
void QQmlModelsModule::defineModule()
{
const char uri[] = "QtQml.Models";
@@ -67,16 +101,23 @@ void QQmlModelsModule::defineModule()
qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel");
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
+
+ qmlRegisterType<QQuickPackage>(uri, 2, 14, "Package");
+ qmlRegisterType<QQmlInstantiator>(uri, 2, 14, "Instantiator");
+ qmlRegisterType<QQmlInstanceModel>();
}
void QQmlModelsModule::defineLabsModule()
{
const char uri[] = "Qt.labs.qmlmodels";
+#if QT_CONFIG(qml_delegate_model)
qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent."));
qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser");
qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice");
+#endif
qmlRegisterType<QQmlTableModel>(uri, 1, 0, "TableModel");
+ qmlRegisterType<QQmlTableModelColumn>(uri, 1, 0, "TableModelColumn");
}
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
index 939ecc1500..2bb04f1e11 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QQmlModelsModule
{
public:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQmlTypes();
+ static void registerQuickTypes();
+#endif
+
static void defineModule();
static void defineLabsModule();
};
diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp
index 2f4d427430..b6330b4295 100644
--- a/src/qml/types/qqmlobjectmodel.cpp
+++ b/src/qml/types/qqmlobjectmodel.cpp
@@ -273,12 +273,12 @@ QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item)
return nullptr;
}
-QString QQmlObjectModel::stringValue(int index, const QString &name)
+QVariant QQmlObjectModel::variantValue(int index, const QString &role)
{
Q_D(QQmlObjectModel);
if (index < 0 || index >= d->children.count())
return QString();
- return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString();
+ return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(role);
}
QQmlIncubator::Status QQmlObjectModel::incubationStatus(int)
diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h
index 4ac4f1c65b..1284ba1780 100644
--- a/src/qml/types/qqmlobjectmodel_p.h
+++ b/src/qml/types/qqmlobjectmodel_p.h
@@ -79,7 +79,8 @@ public:
virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0;
virtual ReleaseFlags release(QObject *object) = 0;
virtual void cancel(int) {}
- virtual QString stringValue(int, const QString &) = 0;
+ QString stringValue(int index, const QString &role) { return variantValue(index, role).toString(); }
+ virtual QVariant variantValue(int, const QString &) = 0;
virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0;
virtual QQmlIncubator::Status incubationStatus(int index) = 0;
@@ -119,7 +120,7 @@ public:
bool isValid() const override;
QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
ReleaseFlags release(QObject *object) override;
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
void setWatchedRoles(const QList<QByteArray> &) override {}
QQmlIncubator::Status incubationStatus(int index) override;
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h
index 3dd5c4e4ce..39ec66d136 100644
--- a/src/qml/types/qqmltableinstancemodel_p.h
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -122,7 +122,7 @@ public:
QQmlIncubator::Status incubationStatus(int index) override;
- QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); }
+ QVariant variantValue(int, const QString &) override { Q_UNREACHABLE(); return QVariant(); }
void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); }
int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; }
diff --git a/src/qml/types/qqmltablemodel.cpp b/src/qml/types/qqmltablemodel.cpp
index 6068155f5a..4a96e7a46b 100644
--- a/src/qml/types/qqmltablemodel.cpp
+++ b/src/qml/types/qqmltablemodel.cpp
@@ -47,27 +47,26 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel")
-static const QString lengthPropertyName = QStringLiteral("length");
-static const QString displayRoleName = QStringLiteral("display");
-
/*!
\qmltype TableModel
\instantiates QQmlTableModel
\inqmlmodule Qt.labs.qmlmodels
\brief Encapsulates a simple table model.
- \since 5.12
-
- The TableModel type stores JavaScript objects as data for a table model
- that can be used with \l TableView.
+ \since 5.14
- The following snippet shows the simplest use case for TableModel:
+ The TableModel type stores JavaScript/JSON objects as data for a table
+ model that can be used with \l TableView. It is intended to support
+ very simple models without requiring the creation of a custom
+ QAbstractTableModel subclass in C++.
\snippet qml/tablemodel/fruit-example-simpledelegate.qml file
- The model's initial data is set with either the \l rows property or by
- calling \l appendRow(). Once the first row has been added to the table, the
- columns and roles are established and will be fixed for the lifetime of the
- model.
+ The model's initial row data is set with either the \l rows property or by
+ calling \l appendRow(). Each column in the model is specified by declaring
+ a \l TableModelColumn instance, where the order of each instance determines
+ its column index. Once the model's \l Component.completed() signal has been
+ emitted, the columns and roles will have been established and are then
+ fixed for the lifetime of the model.
To access a specific row, the \l getRow() function can be used.
It's also possible to access the model's JavaScript data
@@ -87,14 +86,65 @@ static const QString displayRoleName = QStringLiteral("display");
data that is set, it will be automatically converted via
\l {QVariant::canConvert()}{QVariant}.
- For convenience, TableModel provides the \c display role if it is not
- explicitly specified in any column. When a column only has one role
- declared, that role will be used used as the display role. However, when
- there is more than one role in a column, which role will be used is
- undefined. This is because JavaScript does not guarantee that properties
- within an object can be accessed according to the order in which they were
- declared. This is why \c checkable may be used as the display role for the
- first column even though \c checked is declared before it, for example.
+ \section1 Supported Row Data Structures
+
+ TableModel is designed to work with JavaScript/JSON data, where each row
+ is a simple key-pair object:
+
+ \code
+ {
+ // Each property is one cell/column.
+ checked: false,
+ amount: 1,
+ fruitType: "Apple",
+ fruitName: "Granny Smith",
+ fruitPrice: 1.50
+ },
+ // ...
+ \endcode
+
+ As model manipulation in Qt is done via row and column indices,
+ and because object keys are unordered, each column must be specified via
+ TableModelColumn. This allows mapping Qt's built-in roles to any property
+ in each row object.
+
+ Complex row structures are supported, but with limited functionality.
+ As TableModel has no way of knowing how each row is structured,
+ it cannot manipulate it. As a consequence of this, the copy of the
+ model data that TableModel has stored in \l rows is not kept in sync
+ with the source data that was set in QML. For these reasons, TableModel
+ relies on the user to handle simple data manipulation.
+
+ For example, suppose you wanted to have several roles per column. One way
+ of doing this is to use a data source where each row is an array and each
+ cell is an object. To use this data source with TableModel, define a
+ getter and setter:
+
+ \code
+ TableModel {
+ TableModelColumn {
+ display: function(modelIndex) { return rows[modelIndex.row][0].checked }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData }
+ }
+ // ...
+
+ rows: [
+ [
+ { checked: false, checkable: true },
+ { amount: 1 },
+ { fruitType: "Apple" },
+ { fruitName: "Granny Smith" },
+ { fruitPrice: 1.50 }
+ ]
+ // ...
+ ]
+ }
+ \endcode
+
+ The row above is one example of a complex row.
+
+ \note Row manipulation functions such as \l appendRow(), \l removeRow(),
+ etc. are not supported when using complex rows.
\section1 Using DelegateChooser with TableModel
@@ -112,13 +162,12 @@ static const QString displayRoleName = QStringLiteral("display");
\l [QtQuickControls2]{TextField}, and so that delegate is declared
last as a fallback.
- \sa QAbstractTableModel, TableView
+ \sa TableModelColumn, TableView, QAbstractTableModel
*/
QQmlTableModel::QQmlTableModel(QObject *parent)
: QAbstractTableModel(parent)
{
- mRoleNames = QAbstractTableModel::roleNames();
}
QQmlTableModel::~QQmlTableModel()
@@ -153,31 +202,34 @@ void QQmlTableModel::setRows(const QVariant &rows)
return;
}
- QVariant firstRowAsVariant;
- QVariantList firstRow;
- if (!rowsAsVariantList.isEmpty()) {
- // There are rows to validate. If they're not valid,
- // we'll return early without changing anything.
- firstRowAsVariant = rowsAsVariantList.first();
- firstRow = firstRowAsVariant.toList();
+ if (!componentCompleted) {
+ // Store the rows until we can call doSetRows() after component completion.
+ mRows = rowsAsVariantList;
+ return;
+ }
- if (firstRowAsVariant.type() != QVariant::List) {
- qmlWarning(this) << "setRows(): each row in \"rows\" must be an array of objects";
- return;
- }
+ doSetRows(rowsAsVariantList);
+}
- if (mColumnCount > 0) {
- qCDebug(lcTableModel) << "validating" << rowsAsVariantList.size()
- << "rows against existing metadata";
-
- // This is not the first time the rows have been set; validate the new columns.
- for (int i = 0; i < rowsAsVariantList.size(); ++i) {
- // validateNewRow() expects a QVariant wrapping a QJSValue, so to
- // simplify the code, just create one here.
- const QVariant row = QVariant::fromValue(rowsAsJSValue.property(i));
- if (!validateNewRow("setRows()", row, i))
- return;
- }
+void QQmlTableModel::doSetRows(const QVariantList &rowsAsVariantList)
+{
+ Q_ASSERT(componentCompleted);
+
+ // By now, all TableModelColumns should have been set.
+ if (mColumns.isEmpty()) {
+ qmlWarning(this) << "No TableModelColumns were set; model will be empty";
+ return;
+ }
+
+ const bool firstTimeValidRowsHaveBeenSet = mColumnMetadata.isEmpty();
+ if (!firstTimeValidRowsHaveBeenSet) {
+ // This is not the first time rows have been set; validate each one.
+ for (int rowIndex = 0; rowIndex < rowsAsVariantList.size(); ++rowIndex) {
+ // validateNewRow() expects a QVariant wrapping a QJSValue, so to
+ // simplify the code, just create one here.
+ const QVariant row = QVariant::fromValue(rowsAsVariantList.at(rowIndex));
+ if (!validateNewRow("setRows()", row, rowIndex, SetRowsOperation))
+ return;
}
}
@@ -191,59 +243,9 @@ void QQmlTableModel::setRows(const QVariant &rows)
mRows = rowsAsVariantList;
mRowCount = mRows.size();
- const bool isFirstTimeSet = mColumnCount == 0;
- if (isFirstTimeSet && mRowCount > 0) {
- // This is the first time the rows have been set, so establish
- // the column count and gather column metadata.
- mColumnCount = firstRow.size();
- qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:";
-
- // Go through each property of each cell in the first row
- // and make a role name from it.
- int userRoleKey = Qt::UserRole;
- for (int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex) {
- // We need it as a QVariantMap because we need to get
- // the name of the property, which we can't do with QJSValue's API.
- const QVariantMap column = firstRow.at(columnIndex).toMap();
- const QStringList columnPropertyNames = column.keys();
- ColumnProperties properties;
- int propertyInfoIndex = 0;
-
- qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":";
-
- for (const QString &roleName : columnPropertyNames) {
- // QML/JS supports utf8.
- const QByteArray roleNameUtf8 = roleName.toUtf8();
- if (!mRoleNames.values().contains(roleNameUtf8)) {
- // We don't already have this role name, so it's a user role.
- mRoleNames[userRoleKey] = roleName.toUtf8().constData();
- qCDebug(lcTableModel) << " - added new user role" << roleName << "with key" << userRoleKey;
- ++userRoleKey;
- } else {
- qCDebug(lcTableModel) << " - found existing role" << roleName;
- }
-
- if (properties.explicitDisplayRoleIndex == -1 && roleName == displayRoleName) {
- // The user explicitly declared a "display" role,
- // so now we don't need to make it the first role in the column for them.
- properties.explicitDisplayRoleIndex = propertyInfoIndex;
- }
-
- // Keep track of the type of property so we can use it to validate new rows later on.
- const QVariant roleValue = column.value(roleName);
- const auto propertyInfo = ColumnPropertyInfo(roleName, roleValue.type(),
- QString::fromLatin1(roleValue.typeName()));
- properties.infoForProperties.append(propertyInfo);
-
- qCDebug(lcTableModel) << " - column property" << propertyInfo.name
- << "has type" << propertyInfo.typeName;
-
- ++propertyInfoIndex;
- }
-
- mColumnProperties.append(properties);
- }
- }
+ // Gather metadata the first time rows is set.
+ if (firstTimeValidRowsHaveBeenSet && !mRows.isEmpty())
+ fetchColumnMetadata();
endResetModel();
@@ -255,6 +257,94 @@ void QQmlTableModel::setRows(const QVariant &rows)
emit columnCountChanged();
}
+QQmlTableModel::ColumnRoleMetadata QQmlTableModel::fetchColumnRoleData(const QString &roleNameKey,
+ QQmlTableModelColumn *tableModelColumn, int columnIndex) const
+{
+ const QVariant firstRow = mRows.first();
+ ColumnRoleMetadata roleData;
+
+ QJSValue columnRoleGetter = tableModelColumn->getterAtRole(roleNameKey);
+ if (columnRoleGetter.isUndefined()) {
+ // This role is not defined, which is fine; just skip it.
+ return roleData;
+ }
+
+ if (columnRoleGetter.isString()) {
+ // The role is set as a string, so we assume the row is a simple object.
+ if (firstRow.type() != QVariant::Map) {
+ qmlWarning(this).quote() << "expected row for role "
+ << roleNameKey << " of TableModelColumn at index "
+ << columnIndex << " to be a simple object, but it's "
+ << firstRow.typeName() << " instead: " << firstRow;
+ return roleData;
+ }
+ const QVariantMap firstRowAsMap = firstRow.toMap();
+ const QString rolePropertyName = columnRoleGetter.toString();
+ const QVariant roleProperty = firstRowAsMap.value(rolePropertyName);
+
+ roleData.isStringRole = true;
+ roleData.name = rolePropertyName;
+ roleData.type = roleProperty.type();
+ roleData.typeName = QString::fromLatin1(roleProperty.typeName());
+ } else if (columnRoleGetter.isCallable()) {
+ // The role is provided via a function, which means the row is complex and
+ // the user needs to provide the data for it.
+ const auto modelIndex = index(0, columnIndex);
+ const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(modelIndex);
+ const QVariant cellData = columnRoleGetter.call(args).toVariant();
+
+ // We don't know the property name since it's provided through the function.
+ // roleData.name = ???
+ roleData.isStringRole = false;
+ roleData.type = cellData.type();
+ roleData.typeName = QString::fromLatin1(cellData.typeName());
+ } else {
+ // Invalid role.
+ qmlWarning(this) << "TableModelColumn role for column at index "
+ << columnIndex << " must be either a string or a function; actual type is: "
+ << columnRoleGetter.toString();
+ }
+
+ return roleData;
+}
+
+void QQmlTableModel::fetchColumnMetadata()
+{
+ qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:";
+
+ static const auto supportedRoleNames = QQmlTableModelColumn::supportedRoleNames();
+
+ // Since we support different data structures at the row level, we require that there
+ // is a TableModelColumn for each column.
+ // Collect and cache metadata for each column. This makes data lookup faster.
+ for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) {
+ QQmlTableModelColumn *column = mColumns.at(columnIndex);
+ qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":";
+
+ ColumnMetadata metaData;
+ const auto builtInRoleKeys = supportedRoleNames.keys();
+ for (const int builtInRoleKey : builtInRoleKeys) {
+ const QString builtInRoleName = supportedRoleNames.value(builtInRoleKey);
+ ColumnRoleMetadata roleData = fetchColumnRoleData(builtInRoleName, column, columnIndex);
+ if (roleData.type == QVariant::Invalid) {
+ // This built-in role was not specified in this column.
+ continue;
+ }
+
+ qCDebug(lcTableModel).nospace() << " - added metadata for built-in role "
+ << builtInRoleName << " at column index " << columnIndex
+ << ": name=" << roleData.name << " typeName=" << roleData.typeName
+ << " type=" << roleData.type;
+
+ // This column now supports this specific built-in role.
+ metaData.roles.insert(builtInRoleName, roleData);
+ // Add it if it doesn't already exist.
+ mRoleNames[builtInRoleKey] = builtInRoleName.toLatin1();
+ }
+ mColumnMetadata.insert(columnIndex, metaData);
+ }
+}
+
/*!
\qmlmethod TableModel::appendRow(object row)
@@ -262,13 +352,13 @@ void QQmlTableModel::setRows(const QVariant &rows)
values (cells) in \a row.
\code
- model.appendRow([
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ model.appendRow({
+ checkable: true,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
\endcode
\sa insertRow(), setRow(), removeRow()
@@ -306,7 +396,7 @@ void QQmlTableModel::clear()
\code
Component.onCompleted: {
// These two lines are equivalent.
- console.log(model.getRow(0).fruitName);
+ console.log(model.getRow(0).display);
console.log(model.rows[0].fruitName);
}
\endcode
@@ -331,13 +421,13 @@ QVariant QQmlTableModel::getRow(int rowIndex)
values (cells) in \a row.
\code
- model.insertRow(2, [
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ model.insertRow(2, {
+ checkable: true, checked: false,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
\endcode
The \a rowIndex must be to an existing item in the list, or one past
@@ -363,13 +453,32 @@ void QQmlTableModel::doInsert(int rowIndex, const QVariant &row)
mRows.insert(rowIndex, rowAsVariant);
++mRowCount;
- qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index"
- << rowIndex << ":\n" << rowAsVariant.toList();
+ qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index "
+ << rowIndex << ":\n" << rowAsVariant.toMap();
+
+ // Gather metadata the first time a row is added.
+ if (mColumnMetadata.isEmpty())
+ fetchColumnMetadata();
endInsertRows();
emit rowCountChanged();
}
+void QQmlTableModel::classBegin()
+{
+}
+
+void QQmlTableModel::componentComplete()
+{
+ componentCompleted = true;
+
+ mColumnCount = mColumns.size();
+ if (mColumnCount > 0)
+ emit columnCountChanged();
+
+ doSetRows(mRows);
+}
+
/*!
\qmlmethod TableModel::moveRow(int fromRowIndex, int toRowIndex, int rows)
@@ -496,13 +605,13 @@ void QQmlTableModel::removeRow(int rowIndex, int rows)
All columns/cells must be present in \c row, and in the correct order.
\code
- model.setRow(0, [
- { checkable: true, checked: false },
- { amount: 1 },
- { fruitType: "Pear" },
- { fruitName: "Williams" },
- { fruitPrice: 1.50 },
- ])
+ model.setRow(0, {
+ checkable: true,
+ amount: 1,
+ fruitType: "Pear",
+ fruitName: "Williams",
+ fruitPrice: 1.50,
+ })
\endcode
If \a rowIndex is equal to \c rowCount(), then a new row is appended to the
@@ -529,36 +638,40 @@ void QQmlTableModel::setRow(int rowIndex, const QVariant &row)
}
}
-/*!
- \qmlproperty var TableModel::roleDataProvider
-
- This property can hold a function that will map roles to values.
-
- When assigned, it will be called each time data() is called, to enable
- extracting arbitrary values, converting the data in arbitrary ways, or even
- doing calculations. It takes 3 arguments: \c index (\l QModelIndex),
- \c role (string), and \c cellData (object), which is the complete data that
- is stored in the given cell. (If the cell contains a JS object with
- multiple named values, the entire object will be given in \c cellData.)
- The function that you define must return the value to be used; for example
- a typical delegate will display the value returned for the \c display role,
- so you can check whether that is the role and return data in a form that is
- suitable for the delegate to show:
-
- \snippet qml/tablemodel/roleDataProvider.qml 0
-*/
-QJSValue QQmlTableModel::roleDataProvider() const
+QQmlListProperty<QQmlTableModelColumn> QQmlTableModel::columns()
{
- return mRoleDataProvider;
+ return QQmlListProperty<QQmlTableModelColumn>(this, nullptr,
+ &QQmlTableModel::columns_append,
+ &QQmlTableModel::columns_count,
+ &QQmlTableModel::columns_at,
+ &QQmlTableModel::columns_clear);
}
-void QQmlTableModel::setRoleDataProvider(QJSValue roleDataProvider)
+void QQmlTableModel::columns_append(QQmlListProperty<QQmlTableModelColumn> *property,
+ QQmlTableModelColumn *value)
{
- if (roleDataProvider.strictlyEquals(mRoleDataProvider))
- return;
+ QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ QQmlTableModelColumn *column = qobject_cast<QQmlTableModelColumn*>(value);
+ if (column)
+ model->mColumns.append(column);
+}
- mRoleDataProvider = roleDataProvider;
- emit roleDataProviderChanged();
+int QQmlTableModel::columns_count(QQmlListProperty<QQmlTableModelColumn> *property)
+{
+ const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.count();
+}
+
+QQmlTableModelColumn *QQmlTableModel::columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index)
+{
+ const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.at(index);
+}
+
+void QQmlTableModel::columns_clear(QQmlListProperty<QQmlTableModelColumn> *property)
+{
+ QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object);
+ return model->mColumns.clear();
}
/*!
@@ -574,14 +687,19 @@ void QQmlTableModel::setRoleDataProvider(QJSValue roleDataProvider)
TableModel {
id: model
+
+ TableModelColumn { display: "fruitType" }
+ TableModelColumn { display: "fruitPrice" }
+
rows: [
- [{ fruitType: "Apple" }, { fruitPrice: 1.50 }],
- [{ fruitType: "Orange" }, { fruitPrice: 2.50 }]
+ { fruitType: "Apple", fruitPrice: 1.50 },
+ { fruitType: "Orange", fruitPrice: 2.50 }
]
+
Component.onCompleted: {
for (var r = 0; r < model.rowCount; ++r) {
- console.log("An " + model.data(model.index(r, 0)).fruitType +
- " costs " + model.data(model.index(r, 1)).fruitPrice.toFixed(2))
+ console.log("An " + model.data(model.index(r, 0)).display +
+ " costs " + model.data(model.index(r, 1)).display.toFixed(2))
}
}
}
@@ -658,27 +776,31 @@ QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
if (column < 0 || column >= columnCount())
return QVariant();
- if (!mRoleNames.contains(role))
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "setData(): no role named " << roleName
+ << " at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
return QVariant();
+ }
- const QVariantList rowData = mRows.at(row).toList();
-
- if (mRoleDataProvider.isCallable()) {
- auto engine = qmlEngine(this);
- const auto args = QJSValueList() <<
- engine->toScriptValue(index) <<
- QString::fromUtf8(mRoleNames.value(role)) <<
- engine->toScriptValue(rowData.at(column));
- return const_cast<QQmlTableModel*>(this)->mRoleDataProvider.call(args).toVariant();
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ if (roleData.isStringRole) {
+ // We know the data structure, so we can get the data for the user.
+ const QVariantMap rowData = mRows.at(row).toMap();
+ const QString propertyName = columnMetadata.roles.value(roleName).name;
+ const QVariant value = rowData.value(propertyName);
+ return value;
}
- // TODO: should we also allow this code to be executed if roleDataProvider doesn't
- // handle the role/column, so that it only has to handle the case where there is
- // more than one role in a column?
- const QVariantMap columnData = rowData.at(column).toMap();
- const QString propertyName = columnPropertyNameFromRole(column, role);
- const QVariant value = columnData.value(propertyName);
- return value;
+ // We don't know the data structure, so the user has to modify their data themselves.
+ // First, find the getter for this column and role.
+ QJSValue getter = mColumns.at(column)->getterAtRole(roleName);
+
+ // Then, call it and return what it returned.
+ const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index);
+ return getter.call(args).toVariant();
}
/*!
@@ -691,9 +813,9 @@ QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
*/
bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
{
- const int iRole = mRoleNames.key(role.toUtf8(), -1);
- if (iRole >= 0)
- return setData(index, value, iRole);
+ const int intRole = mRoleNames.key(role.toUtf8(), -1);
+ if (intRole >= 0)
+ return setData(index, value, intRole);
return false;
}
@@ -707,56 +829,92 @@ bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, in
if (column < 0 || column >= columnCount())
return false;
- if (!mRoleNames.contains(role))
- return false;
-
- const QVariantList rowData = mRows.at(row).toList();
- const QString propertyName = columnPropertyNameFromRole(column, role);
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
qCDebug(lcTableModel).nospace() << "setData() called with index "
- << index << ", value " << value << " and role " << propertyName;
+ << index << ", value " << value << " and role " << roleName;
// Verify that the role exists for this column.
- const ColumnPropertyInfo propertyInfo = findColumnPropertyInfo(column, propertyName);
- if (!propertyInfo.isValid()) {
- QString message;
- QDebug stream(&message);
- stream.nospace() << "setData(): no role named " << propertyName
- << " at column index " << column << ". The available roles for that column are:\n";
-
- const QVector<ColumnPropertyInfo> availableProperties = mColumnProperties.at(column).infoForProperties;
- for (auto propertyInfo : availableProperties)
- stream << " - " << propertyInfo.name << " (" << qPrintable(propertyInfo.typeName) << ")";
-
- qmlWarning(this) << message;
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "setData(): no role named \"" << roleName
+ << "\" at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
return false;
}
// Verify that the type of the value is what we expect.
// If the value set is not of the expected type, we can try to convert it automatically.
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
QVariant effectiveValue = value;
- if (value.type() != propertyInfo.type) {
- if (!value.canConvert(int(propertyInfo.type))) {
+ if (value.type() != roleData.type) {
+ if (!value.canConvert(int(roleData.type))) {
qmlWarning(this).nospace() << "setData(): the value " << value
- << " set at row " << row << " column " << column << " with role " << propertyName
- << " cannot be converted to " << propertyInfo.typeName;
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " cannot be converted to " << roleData.typeName;
return false;
}
- if (!effectiveValue.convert(int(propertyInfo.type))) {
+ if (!effectiveValue.convert(int(roleData.type))) {
qmlWarning(this).nospace() << "setData(): failed converting value " << value
- << " set at row " << row << " column " << column << " with role " << propertyName
- << " to " << propertyInfo.typeName;
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " to " << roleData.typeName;
return false;
}
}
- QVariantMap modifiedColumn = rowData.at(column).toMap();
- modifiedColumn[propertyName] = value;
+ if (roleData.isStringRole) {
+ // We know the data structure, so we can set it for the user.
+ QVariantMap modifiedRow = mRows.at(row).toMap();
+ modifiedRow[roleData.name] = value;
+
+ mRows[row] = modifiedRow;
+ } else {
+ // We don't know the data structure, so the user has to modify their data themselves.
+ auto engine = qmlEngine(this);
+ auto args = QJSValueList()
+ // arg 0: modelIndex.
+ << engine->toScriptValue(index)
+ // arg 1: cellData.
+ << engine->toScriptValue(value);
+ // Do the actual setting.
+ QJSValue setter = mColumns.at(column)->setterAtRole(roleName);
+ setter.call(args);
+
+ /*
+ The chain of events so far:
- QVariantList modifiedRow = rowData;
- modifiedRow[column] = modifiedColumn;
- mRows[row] = modifiedRow;
+ - User did e.g.: model.edit = textInput.text
+ - setData() is called
+ - setData() calls the setter
+ (remember that we need to emit the dataChanged() signal,
+ which is why the user can't just set the data directly in the delegate)
+
+ Now the user's setter function has modified *their* copy of the
+ data, but *our* copy of the data is old. Imagine the getters and setters looked like this:
+
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+ setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData }
+
+ We don't know the structure of the user's data, so we can't just do
+ what we do above for the isStringRole case:
+
+ modifiedRow[column][roleName] = value
+
+ This means that, besides getting the implicit row count when rows is initially set,
+ our copy of the data is unused when it comes to complex columns.
+
+ Another point to note is that we can't pass rowData in to the getter as a convenience,
+ because we would be passing in *our* copy of the row, which is not up-to-date.
+ Since the user already has access to the data, it's not a big deal for them to do:
+
+ display: function(modelIndex) { return rows[modelIndex.row][1].amount }
+
+ instead of:
+
+ display: function(modelIndex, rowData) { return rowData[1].amount }
+ */
+ }
QVector<int> rolesChanged;
rolesChanged.append(role);
@@ -770,35 +928,36 @@ QHash<int, QByteArray> QQmlTableModel::roleNames() const
return mRoleNames;
}
-QQmlTableModel::ColumnPropertyInfo::ColumnPropertyInfo()
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata()
{
}
-QQmlTableModel::ColumnPropertyInfo::ColumnPropertyInfo(
- const QString &name, QVariant::Type type, const QString &typeName) :
+QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata(
+ bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName) :
+ isStringRole(isStringRole),
name(name),
type(type),
typeName(typeName)
{
}
-bool QQmlTableModel::ColumnPropertyInfo::isValid() const
+bool QQmlTableModel::ColumnRoleMetadata::isValid() const
{
return !name.isEmpty();
}
bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &row) const
{
- if (row.userType() != qMetaTypeId<QJSValue>()) {
- qmlWarning(this) << functionName << ": expected \"row\" argument to be an array,"
- << " but got " << row.typeName() << " instead";
+ if (!row.canConvert<QJSValue>()) {
+ qmlWarning(this) << functionName << ": expected \"row\" argument to be a QJSValue,"
+ << " but got " << row.typeName() << " instead:\n" << row;
return false;
}
- const QVariant rowAsVariant = row.value<QJSValue>().toVariant();
- if (rowAsVariant.type() != QVariant::List) {
- qmlWarning(this) << functionName << ": expected \"row\" argument to be an array,"
- << " but got " << row.typeName() << " instead";
+ const QJSValue rowAsJSValue = row.value<QJSValue>();
+ if (!rowAsJSValue.isObject() && !rowAsJSValue.isArray()) {
+ qmlWarning(this) << functionName << ": expected \"row\" argument "
+ << "to be an object or array, but got:\n" << rowAsJSValue.toString();
return false;
}
@@ -806,12 +965,21 @@ bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &r
}
bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &row,
- int rowIndex, NewRowOperationFlag appendFlag) const
+ int rowIndex, NewRowOperationFlag operation) const
{
- if (!validateRowType(functionName, row))
+ if (mColumnMetadata.isEmpty()) {
+ // There is no column metadata, so we have nothing to validate the row against.
+ // Rows have to be added before we can gather metadata from them, so just this
+ // once we'll return true to allow the rows to be added.
+ return true;
+ }
+
+ // Don't require each row to be a QJSValue when setting all rows,
+ // as they won't be; they'll be QVariantMap.
+ if (operation != SetRowsOperation && !validateRowType(functionName, row))
return false;
- if (appendFlag == OtherOperation) {
+ if (operation == OtherOperation) {
// Inserting/setting.
if (rowIndex < 0) {
qmlWarning(this) << functionName << ": \"rowIndex\" cannot be negative";
@@ -825,29 +993,48 @@ bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &ro
}
}
- const QVariant rowAsVariant = row.value<QJSValue>().toVariant();
- const QVariantList rowAsList = rowAsVariant.toList();
+ const QVariant rowAsVariant = operation == SetRowsOperation
+ ? row : row.value<QJSValue>().toVariant();
+ if (rowAsVariant.type() != QVariant::Map) {
+ qmlWarning(this) << functionName << ": row manipulation functions "
+ << "do not support complex rows (row index: " << rowIndex << ")";
+ return false;
+ }
- const int columnCount = rowAsList.size();
- if (columnCount != mColumnCount) {
+ const QVariantMap rowAsMap = rowAsVariant.toMap();
+ const int columnCount = rowAsMap.size();
+ if (columnCount < mColumnCount) {
qmlWarning(this) << functionName << ": expected " << mColumnCount
- << " columns, but got " << columnCount;
+ << " columns, but only got " << columnCount;
return false;
}
- // Verify that the row's columns and their roles match the name and type of existing data.
- // This iterates across the columns in the row. For example:
- // [
- // { checkable: true, checked: false }, // columnIndex == 0
- // { amount: 1 }, // columnIndex == 1
- // { fruitType: "Orange" }, // etc.
- // { fruitName: "Navel" },
- // { fruitPrice: 2.50 }
- // ],
- for (int columnIndex = 0; columnIndex < mColumnCount; ++columnIndex) {
- const QVariantMap column = rowAsList.at(columnIndex).toMap();
- if (!validateColumnPropertyTypes(functionName, column, columnIndex))
- return false;
+ // We can't validate complex structures, but we can make sure that
+ // each simple string-based role in each column is correct.
+ for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) {
+ QQmlTableModelColumn *column = mColumns.at(columnIndex);
+ const QHash<QString, QJSValue> getters = column->getters();
+ const auto roleNames = getters.keys();
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(columnIndex);
+ for (const QString &roleName : roleNames) {
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ if (!roleData.isStringRole)
+ continue;
+
+ if (!rowAsMap.contains(roleData.name)) {
+ qmlWarning(this).quote() << functionName << ": expected a property named "
+ << roleData.name << " in row at index " << rowIndex << ", but couldn't find one";
+ return false;
+ }
+
+ const QVariant rolePropertyValue = rowAsMap.value(roleData.name);
+ if (rolePropertyValue.type() != roleData.type) {
+ qmlWarning(this).quote() << functionName << ": expected the property named "
+ << roleData.name << " to be of type " << roleData.typeName
+ << ", but got " << QString::fromLatin1(rolePropertyValue.typeName()) << " instead";
+ return false;
+ }
+ }
}
return true;
@@ -869,82 +1056,4 @@ bool QQmlTableModel::validateRowIndex(const char *functionName, const char *argu
return true;
}
-bool QQmlTableModel::validateColumnPropertyTypes(const char *functionName,
- const QVariantMap &column, int columnIndex) const
-{
- // Actual
- const QVariantList columnProperties = column.values();
- const QStringList propertyNames = column.keys();
- // Expected
- const QVector<ColumnPropertyInfo> properties = mColumnProperties.at(columnIndex).infoForProperties;
-
- // This iterates across the properties in the column. For example:
- // 0 1 2
- // { foo: "A", bar: 1, baz: true },
- for (int propertyIndex = 0; propertyIndex < properties.size(); ++propertyIndex) {
- const QString propertyName = propertyNames.at(propertyIndex);
- const QVariant propertyValue = columnProperties.at(propertyIndex);
- const ColumnPropertyInfo expectedPropertyFormat = properties.at(propertyIndex);
-
- if (!validateColumnPropertyType(functionName, propertyName,
- propertyValue, expectedPropertyFormat, columnIndex)) {
- return false;
- }
- }
-
- return true;
-}
-
-bool QQmlTableModel::validateColumnPropertyType(const char *functionName, const QString &propertyName,
- const QVariant &propertyValue, const ColumnPropertyInfo &expectedPropertyFormat, int columnIndex) const
-{
- if (propertyName != expectedPropertyFormat.name) {
- qmlWarning(this) << functionName
- << ": expected property named " << expectedPropertyFormat.name
- << " at column index " << columnIndex
- << ", but got " << propertyName << " instead";
- return false;
- }
-
- if (propertyValue.type() != expectedPropertyFormat.type) {
- qmlWarning(this) << functionName
- << ": expected property with type " << expectedPropertyFormat.typeName
- << " at column index " << columnIndex
- << ", but got " << propertyValue.typeName() << " instead";
- return false;
- }
-
- return true;
-}
-
-QQmlTableModel::ColumnPropertyInfo QQmlTableModel::findColumnPropertyInfo(
- int columnIndex, const QString &columnPropertyName) const
-{
- // TODO: check if a hash with its string-based lookup is faster,
- // keeping in mind that we may be doing index-based lookups too.
- const QVector<ColumnPropertyInfo> properties = mColumnProperties.at(columnIndex).infoForProperties;
- for (int i = 0; i < properties.size(); ++i) {
- const ColumnPropertyInfo &info = properties.at(i);
- if (info.name == columnPropertyName)
- return info;
- }
-
- return ColumnPropertyInfo();
-}
-
-QString QQmlTableModel::columnPropertyNameFromRole(int columnIndex, int role) const
-{
- QString propertyName;
- if (role == Qt::DisplayRole && mColumnProperties.at(columnIndex).explicitDisplayRoleIndex == -1) {
- // The user is getting or setting data for the display role,
- // but didn't specify any role with the name "display" in this column.
- // So, we give them the implicit display role, aka the first property we find.
- propertyName = mColumnProperties.at(columnIndex).infoForProperties.first().name;
- } else {
- // QML/JS supports utf8.
- propertyName = QString::fromUtf8(mRoleNames.value(role));
- }
- return propertyName;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltablemodel_p.h b/src/qml/types/qqmltablemodel_p.h
index 33b2189fcd..a1bb97e7d4 100644
--- a/src/qml/types/qqmltablemodel_p.h
+++ b/src/qml/types/qqmltablemodel_p.h
@@ -55,17 +55,21 @@
#include <QtCore/QAbstractTableModel>
#include <QtQml/qqml.h>
#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQml/private/qqmltablemodelcolumn_p.h>
#include <QtQml/QJSValue>
+#include <QtQml/QQmlListProperty>
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel
+class Q_QML_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus
{
Q_OBJECT
Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL)
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged FINAL)
Q_PROPERTY(QVariant rows READ rows WRITE setRows NOTIFY rowsChanged FINAL)
- Q_PROPERTY(QJSValue roleDataProvider READ roleDataProvider WRITE setRoleDataProvider NOTIFY roleDataProviderChanged)
+ Q_PROPERTY(QQmlListProperty<QQmlTableModelColumn> columns READ columns CONSTANT FINAL)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_CLASSINFO("DefaultProperty", "columns")
public:
QQmlTableModel(QObject *parent = nullptr);
@@ -82,8 +86,12 @@ public:
Q_INVOKABLE void removeRow(int rowIndex, int rows = 1);
Q_INVOKABLE void setRow(int rowIndex, const QVariant &row);
- QJSValue roleDataProvider() const;
- void setRoleDataProvider(QJSValue roleDataProvider);
+ QQmlListProperty<QQmlTableModelColumn> columns();
+
+ static void columns_append(QQmlListProperty<QQmlTableModelColumn> *property, QQmlTableModelColumn *value);
+ static int columns_count(QQmlListProperty<QQmlTableModelColumn> *property);
+ static QQmlTableModelColumn *columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index);
+ static void columns_clear(QQmlListProperty<QQmlTableModelColumn> *property);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -98,57 +106,61 @@ Q_SIGNALS:
void columnCountChanged();
void rowCountChanged();
void rowsChanged();
- void roleDataProviderChanged();
private:
- class ColumnPropertyInfo
+ class ColumnRoleMetadata
{
public:
- ColumnPropertyInfo();
- ColumnPropertyInfo(const QString &name, QVariant::Type type, const QString &typeName);
+ ColumnRoleMetadata();
+ ColumnRoleMetadata(bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName);
bool isValid() const;
+ // If this is false, it's a function role.
+ bool isStringRole = false;
QString name;
QVariant::Type type = QVariant::Invalid;
QString typeName;
};
- struct ColumnProperties
+ struct ColumnMetadata
{
- QVector<ColumnPropertyInfo> infoForProperties;
- // If there was a display role found in this column, it'll be stored here.
- // The index is into infoForProperties.
- int explicitDisplayRoleIndex = -1;
+ // Key = role name that will be made visible to the delegate
+ // Value = metadata about that role, including actual name in the model data, type, etc.
+ QHash<QString, ColumnRoleMetadata> roles;
};
enum NewRowOperationFlag {
OtherOperation, // insert(), set(), etc.
+ SetRowsOperation,
AppendOperation
};
+ void doSetRows(const QVariantList &rowsAsVariantList);
+ ColumnRoleMetadata fetchColumnRoleData(const QString &roleNameKey,
+ QQmlTableModelColumn *tableModelColumn, int columnIndex) const;
+ void fetchColumnMetadata();
+
bool validateRowType(const char *functionName, const QVariant &row) const;
bool validateNewRow(const char *functionName, const QVariant &row,
- int rowIndex, NewRowOperationFlag appendFlag = OtherOperation) const;
+ int rowIndex, NewRowOperationFlag operation = OtherOperation) const;
bool validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const;
- bool validateColumnPropertyTypes(const char *functionName, const QVariantMap &column, int columnIndex) const;
- bool validateColumnPropertyType(const char *functionName, const QString &propertyName,
- const QVariant &propertyValue, const ColumnPropertyInfo &expectedPropertyFormat, int columnIndex) const;
-
- ColumnPropertyInfo findColumnPropertyInfo(int columnIndex, const QString &columnPropertyNameFromRole) const;
- QString columnPropertyNameFromRole(int columnIndex, int role) const;
void doInsert(int rowIndex, const QVariant &row);
+ void classBegin() override;
+ void componentComplete() override;
+
+ bool componentCompleted = false;
QVariantList mRows;
+ QList<QQmlTableModelColumn *> mColumns;
int mRowCount = 0;
int mColumnCount = 0;
// Each entry contains information about the properties of the column at that index.
- QVector<ColumnProperties> mColumnProperties;
+ QVector<ColumnMetadata> mColumnMetadata;
// key = property index (0 to number of properties across all columns)
// value = role name
QHash<int, QByteArray> mRoleNames;
- QJSValue mRoleDataProvider;
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltablemodelcolumn.cpp b/src/qml/types/qqmltablemodelcolumn.cpp
new file mode 100644
index 0000000000..93da0642de
--- /dev/null
+++ b/src/qml/types/qqmltablemodelcolumn.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltablemodelcolumn_p.h"
+
+#include <QtQml/qqmlinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype TableModelColumn
+ \instantiates QQmlTableModelColumn
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Represents a column in a model.
+ \since 5.14
+
+ \section1 Supported Roles
+
+ TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles},
+ with the exception of \c Qt::InitialSortOrderRole.
+
+ \sa TableModel, TableView
+*/
+
+static const QString displayRoleName = QStringLiteral("display");
+static const QString decorationRoleName = QStringLiteral("decoration");
+static const QString editRoleName = QStringLiteral("edit");
+static const QString toolTipRoleName = QStringLiteral("toolTip");
+static const QString statusTipRoleName = QStringLiteral("statusTip");
+static const QString whatsThisRoleName = QStringLiteral("whatsThis");
+
+static const QString fontRoleName = QStringLiteral("font");
+static const QString textAlignmentRoleName = QStringLiteral("textAlignment");
+static const QString backgroundRoleName = QStringLiteral("background");
+static const QString foregroundRoleName = QStringLiteral("foreground");
+static const QString checkStateRoleName = QStringLiteral("checkState");
+
+static const QString accessibleTextRoleName = QStringLiteral("accessibleText");
+static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription");
+
+static const QString sizeHintRoleName = QStringLiteral("sizeHint");
+
+
+QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QQmlTableModelColumn::~QQmlTableModelColumn()
+{
+}
+
+#define DEFINE_ROLE_PROPERTIES(getterGetterName, getterSetterName, getterSignal, setterGetterName, setterSetterName, setterSignal, roleName) \
+QJSValue QQmlTableModelColumn::getterGetterName() const \
+{ \
+ return mGetters.value(roleName); \
+} \
+\
+void QQmlTableModelColumn::getterSetterName(const QJSValue &stringOrFunction) \
+{ \
+ if (!stringOrFunction.isString() && !stringOrFunction.isCallable()) { \
+ qmlWarning(this).quote() << "getter for " << roleName << " must be a function"; \
+ return; \
+ } \
+ if (stringOrFunction.strictlyEquals(decoration())) \
+ return; \
+\
+ mGetters[roleName] = stringOrFunction; \
+ emit decorationChanged(); \
+} \
+\
+QJSValue QQmlTableModelColumn::setterGetterName() const \
+{ \
+ return mSetters.value(roleName); \
+} \
+\
+void QQmlTableModelColumn::setterSetterName(const QJSValue &function) \
+{ \
+ if (!function.isCallable()) { \
+ qmlWarning(this).quote() << "setter for " << roleName << " must be a function"; \
+ return; \
+ } \
+\
+ if (function.strictlyEquals(getSetDisplay())) \
+ return; \
+\
+ mSetters[roleName] = function; \
+ emit setDisplayChanged(); \
+}
+
+DEFINE_ROLE_PROPERTIES(display, setDisplay, displayChanged,
+ getSetDisplay, setSetDisplay, setDisplayChanged, displayRoleName)
+DEFINE_ROLE_PROPERTIES(decoration, setDecoration, decorationChanged,
+ getSetDecoration, setSetDecoration, setDecorationChanged, decorationRoleName)
+DEFINE_ROLE_PROPERTIES(edit, setEdit, editChanged,
+ getSetEdit, setSetEdit, setEditChanged, editRoleName)
+DEFINE_ROLE_PROPERTIES(toolTip, setToolTip, toolTipChanged,
+ getSetToolTip, setSetToolTip, setToolTipChanged, toolTipRoleName)
+DEFINE_ROLE_PROPERTIES(statusTip, setStatusTip, statusTipChanged,
+ getSetStatusTip, setSetStatusTip, setStatusTipChanged, statusTipRoleName)
+DEFINE_ROLE_PROPERTIES(whatsThis, setWhatsThis, whatsThisChanged,
+ getSetWhatsThis, setSetWhatsThis, setWhatsThisChanged, whatsThisRoleName)
+
+DEFINE_ROLE_PROPERTIES(font, setFont, fontChanged,
+ getSetFont, setSetFont, setFontChanged, fontRoleName)
+DEFINE_ROLE_PROPERTIES(textAlignment, setTextAlignment, textAlignmentChanged,
+ getSetTextAlignment, setSetTextAlignment, setTextAlignmentChanged, textAlignmentRoleName)
+DEFINE_ROLE_PROPERTIES(background, setBackground, backgroundChanged,
+ getSetBackground, setSetBackground, setBackgroundChanged, backgroundRoleName)
+DEFINE_ROLE_PROPERTIES(foreground, setForeground, foregroundChanged,
+ getSetForeground, setSetForeground, setForegroundChanged, foregroundRoleName)
+DEFINE_ROLE_PROPERTIES(checkState, setCheckState, checkStateChanged,
+ getSetCheckState, setSetCheckState, setCheckStateChanged, checkStateRoleName)
+
+DEFINE_ROLE_PROPERTIES(accessibleText, setAccessibleText, accessibleTextChanged,
+ getSetAccessibleText, setSetAccessibleText, setAccessibleTextChanged, accessibleTextRoleName)
+DEFINE_ROLE_PROPERTIES(accessibleDescription, setAccessibleDescription, accessibleDescriptionChanged,
+ getSetAccessibleDescription, setSetAccessibleDescription, setAccessibleDescriptionChanged, accessibleDescriptionRoleName)
+
+DEFINE_ROLE_PROPERTIES(sizeHint, setSizeHint, sizeHintChanged,
+ getSetSizeHint, setSetSizeHint, setSizeHintChanged, sizeHintRoleName)
+
+QJSValue QQmlTableModelColumn::getterAtRole(const QString &roleName)
+{
+ auto it = mGetters.find(roleName);
+ if (it == mGetters.end())
+ return QJSValue();
+ return *it;
+}
+
+QJSValue QQmlTableModelColumn::setterAtRole(const QString &roleName)
+{
+ auto it = mSetters.find(roleName);
+ if (it == mSetters.end())
+ return QJSValue();
+ return *it;
+}
+
+const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const
+{
+ return mGetters;
+}
+
+const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames()
+{
+ QHash<int, QString> names;
+ names[Qt::DisplayRole] = QLatin1String("display");
+ names[Qt::DecorationRole] = QLatin1String("decoration");
+ names[Qt::EditRole] = QLatin1String("edit");
+ names[Qt::ToolTipRole] = QLatin1String("toolTip");
+ names[Qt::StatusTipRole] = QLatin1String("statusTip");
+ names[Qt::WhatsThisRole] = QLatin1String("whatsThis");
+ names[Qt::FontRole] = QLatin1String("font");
+ names[Qt::TextAlignmentRole] = QLatin1String("textAlignment");
+ names[Qt::BackgroundRole] = QLatin1String("background");
+ names[Qt::ForegroundRole] = QLatin1String("foreground");
+ names[Qt::CheckStateRole] = QLatin1String("checkState");
+ names[Qt::AccessibleTextRole] = QLatin1String("accessibleText");
+ names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription");
+ names[Qt::SizeHintRole] = QLatin1String("sizeHint");
+ return names;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltablemodelcolumn_p.h b/src/qml/types/qqmltablemodelcolumn_p.h
new file mode 100644
index 0000000000..41c02482c0
--- /dev/null
+++ b/src/qml/types/qqmltablemodelcolumn_p.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTABLEMODELCOLUMN_P_H
+#define QQMLTABLEMODELCOLUMN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QObject>
+#include <QtQml/qqml.h>
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQml/qjsvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_AUTOTEST_EXPORT QQmlTableModelColumn : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QJSValue display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
+ Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged)
+ Q_PROPERTY(QJSValue decoration READ decoration WRITE setDecoration NOTIFY decorationChanged FINAL)
+ Q_PROPERTY(QJSValue setDecoration READ getSetDecoration WRITE setSetDecoration NOTIFY setDecorationChanged FINAL)
+ Q_PROPERTY(QJSValue edit READ edit WRITE setEdit NOTIFY editChanged FINAL)
+ Q_PROPERTY(QJSValue setEdit READ getSetEdit WRITE setSetEdit NOTIFY setEditChanged FINAL)
+ Q_PROPERTY(QJSValue toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged FINAL)
+ Q_PROPERTY(QJSValue setToolTip READ getSetToolTip WRITE setSetToolTip NOTIFY setToolTipChanged FINAL)
+ Q_PROPERTY(QJSValue statusTip READ statusTip WRITE setStatusTip NOTIFY statusTipChanged FINAL)
+ Q_PROPERTY(QJSValue setStatusTip READ getSetStatusTip WRITE setSetStatusTip NOTIFY setStatusTipChanged FINAL)
+ Q_PROPERTY(QJSValue whatsThis READ whatsThis WRITE setWhatsThis NOTIFY whatsThisChanged FINAL)
+ Q_PROPERTY(QJSValue setWhatsThis READ getSetWhatsThis WRITE setSetWhatsThis NOTIFY setWhatsThisChanged FINAL)
+
+ Q_PROPERTY(QJSValue font READ font WRITE setFont NOTIFY fontChanged FINAL)
+ Q_PROPERTY(QJSValue setFont READ getSetFont WRITE setSetFont NOTIFY setFontChanged FINAL)
+ Q_PROPERTY(QJSValue textAlignment READ textAlignment WRITE setTextAlignment NOTIFY textAlignmentChanged FINAL)
+ Q_PROPERTY(QJSValue setTextAlignment READ getSetTextAlignment WRITE setSetTextAlignment NOTIFY setTextAlignmentChanged FINAL)
+ Q_PROPERTY(QJSValue background READ background WRITE setBackground NOTIFY backgroundChanged FINAL)
+ Q_PROPERTY(QJSValue setBackground READ getSetBackground WRITE setSetBackground NOTIFY setBackgroundChanged FINAL)
+ Q_PROPERTY(QJSValue foreground READ foreground WRITE setForeground NOTIFY foregroundChanged FINAL)
+ Q_PROPERTY(QJSValue setForeground READ getSetForeground WRITE setSetForeground NOTIFY setForegroundChanged FINAL)
+ Q_PROPERTY(QJSValue checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL)
+ Q_PROPERTY(QJSValue setCheckState READ getSetCheckState WRITE setSetCheckState NOTIFY setCheckStateChanged FINAL)
+
+ Q_PROPERTY(QJSValue accessibleText READ accessibleText WRITE setAccessibleText NOTIFY accessibleTextChanged FINAL)
+ Q_PROPERTY(QJSValue setAccessibleText READ getSetAccessibleText WRITE setSetAccessibleText NOTIFY setAccessibleTextChanged FINAL)
+ Q_PROPERTY(QJSValue accessibleDescription READ accessibleDescription
+ WRITE setAccessibleDescription NOTIFY accessibleDescriptionChanged FINAL)
+ Q_PROPERTY(QJSValue setAccessibleDescription READ getSetAccessibleDescription
+ WRITE setSetAccessibleDescription NOTIFY setAccessibleDescriptionChanged FINAL)
+
+ Q_PROPERTY(QJSValue sizeHint READ sizeHint WRITE setSizeHint NOTIFY sizeHintChanged FINAL)
+ Q_PROPERTY(QJSValue setSizeHint READ getSetSizeHint WRITE setSetSizeHint NOTIFY setSizeHintChanged FINAL)
+
+public:
+ QQmlTableModelColumn(QObject *parent = nullptr);
+ ~QQmlTableModelColumn() override;
+
+ QJSValue display() const;
+ void setDisplay(const QJSValue &stringOrFunction);
+ QJSValue getSetDisplay() const;
+ void setSetDisplay(const QJSValue &function);
+
+ QJSValue decoration() const;
+ void setDecoration(const QJSValue &stringOrFunction);
+ QJSValue getSetDecoration() const;
+ void setSetDecoration(const QJSValue &function);
+
+ QJSValue edit() const;
+ void setEdit(const QJSValue &stringOrFunction);
+ QJSValue getSetEdit() const;
+ void setSetEdit(const QJSValue &function);
+
+ QJSValue toolTip() const;
+ void setToolTip(const QJSValue &stringOrFunction);
+ QJSValue getSetToolTip() const;
+ void setSetToolTip(const QJSValue &function);
+
+ QJSValue statusTip() const;
+ void setStatusTip(const QJSValue &stringOrFunction);
+ QJSValue getSetStatusTip() const;
+ void setSetStatusTip(const QJSValue &function);
+
+ QJSValue whatsThis() const;
+ void setWhatsThis(const QJSValue &stringOrFunction);
+ QJSValue getSetWhatsThis() const;
+ void setSetWhatsThis(const QJSValue &function);
+
+ QJSValue font() const;
+ void setFont(const QJSValue &stringOrFunction);
+ QJSValue getSetFont() const;
+ void setSetFont(const QJSValue &function);
+
+ QJSValue textAlignment() const;
+ void setTextAlignment(const QJSValue &stringOrFunction);
+ QJSValue getSetTextAlignment() const;
+ void setSetTextAlignment(const QJSValue &function);
+
+ QJSValue background() const;
+ void setBackground(const QJSValue &stringOrFunction);
+ QJSValue getSetBackground() const;
+ void setSetBackground(const QJSValue &function);
+
+ QJSValue foreground() const;
+ void setForeground(const QJSValue &stringOrFunction);
+ QJSValue getSetForeground() const;
+ void setSetForeground(const QJSValue &function);
+
+ QJSValue checkState() const;
+ void setCheckState(const QJSValue &stringOrFunction);
+ QJSValue getSetCheckState() const;
+ void setSetCheckState(const QJSValue &function);
+
+ QJSValue accessibleText() const;
+ void setAccessibleText(const QJSValue &stringOrFunction);
+ QJSValue getSetAccessibleText() const;
+ void setSetAccessibleText(const QJSValue &function);
+
+ QJSValue accessibleDescription() const;
+ void setAccessibleDescription(const QJSValue &stringOrFunction);
+ QJSValue getSetAccessibleDescription() const;
+ void setSetAccessibleDescription(const QJSValue &function);
+
+ QJSValue sizeHint() const;
+ void setSizeHint(const QJSValue &stringOrFunction);
+ QJSValue getSetSizeHint() const;
+ void setSetSizeHint(const QJSValue &function);
+
+ QJSValue getterAtRole(const QString &roleName);
+ QJSValue setterAtRole(const QString &roleName);
+
+ const QHash<QString, QJSValue> getters() const;
+
+ static const QHash<int, QString> supportedRoleNames();
+
+Q_SIGNALS:
+ void indexChanged();
+ void displayChanged();
+ void setDisplayChanged();
+ void decorationChanged();
+ void setDecorationChanged();
+ void editChanged();
+ void setEditChanged();
+ void toolTipChanged();
+ void setToolTipChanged();
+ void statusTipChanged();
+ void setStatusTipChanged();
+ void whatsThisChanged();
+ void setWhatsThisChanged();
+
+ void fontChanged();
+ void setFontChanged();
+ void textAlignmentChanged();
+ void setTextAlignmentChanged();
+ void backgroundChanged();
+ void setBackgroundChanged();
+ void foregroundChanged();
+ void setForegroundChanged();
+ void checkStateChanged();
+ void setCheckStateChanged();
+
+ void accessibleTextChanged();
+ void setAccessibleTextChanged();
+ void accessibleDescriptionChanged();
+ void setAccessibleDescriptionChanged();
+ void sizeHintChanged();
+ void setSizeHintChanged();
+
+private:
+ int mIndex = -1;
+
+ // We store these in hashes because QQuickTableModel needs string-based lookup in certain situations.
+ QHash<QString, QJSValue> mGetters;
+ QHash<QString, QJSValue> mSetters;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlTableModelColumn)
+
+#endif // QQMLTABLEMODELCOLUMN_P_H
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index edb112276c..c081c9e7fc 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -39,10 +39,6 @@
#include "qtqmlglobal_p.h"
#include "qquickworkerscript_p.h"
-#if QT_CONFIG(qml_list_model)
-#include "qqmllistmodel_p.h"
-#include "qqmllistmodelworkeragent_p.h"
-#endif
#include <private/qqmlengine_p.h>
#include <private/qqmlexpression_p.h>
@@ -651,12 +647,10 @@ void QQuickWorkerScript::componentComplete()
bool QQuickWorkerScript::event(QEvent *event)
{
if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
- QQmlEngine *engine = qmlEngine(this);
- if (engine) {
+ if (QQmlEngine *engine = qmlEngine(this)) {
+ QV4::ExecutionEngine *v4 = engine->handle();
WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
- QV4::Scope scope(engine->handle());
- QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine));
- emit message(QQmlV4Handle(value));
+ emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4)));
}
return true;
} else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) {
diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h
index 1a8d2ab076..87cf2e9754 100644
--- a/src/qml/types/qquickworkerscript_p.h
+++ b/src/qml/types/qquickworkerscript_p.h
@@ -83,7 +83,6 @@ private:
};
class QQmlV4Function;
-class QQmlV4Handle;
class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
{
Q_OBJECT
@@ -102,7 +101,7 @@ public Q_SLOTS:
Q_SIGNALS:
void sourceChanged();
- void message(const QQmlV4Handle &messageObject);
+ void message(const QJSValue &messageObject);
protected:
void classBegin() override;
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index 1765beb09e..5a56208dc4 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -7,7 +7,8 @@ SOURCES += \
$$PWD/qquickpackage.cpp \
$$PWD/qqmlinstantiator.cpp \
$$PWD/qqmltableinstancemodel.cpp \
- $$PWD/qqmltablemodel.cpp
+ $$PWD/qqmltablemodel.cpp \
+ $$PWD/qqmltablemodelcolumn.cpp
HEADERS += \
$$PWD/qqmlbind_p.h \
@@ -19,7 +20,8 @@ HEADERS += \
$$PWD/qqmlinstantiator_p.h \
$$PWD/qqmlinstantiator_p_p.h \
$$PWD/qqmltableinstancemodel_p.h \
- $$PWD/qqmltablemodel_p.h
+ $$PWD/qqmltablemodel_p.h \
+ $$PWD/qqmltablemodelcolumn_p.h
qtConfig(qml-worker-script) {
SOURCES += \
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index d9cb6506b8..f991ae0a69 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -42,7 +42,6 @@
#include <private/qqmldelegatemodel_p_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qqmlproperty_p.h>
-#include <private/qv8engine_p.h>
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
@@ -525,7 +524,7 @@ public:
metaObject.reset(builder.toMetaObject());
*static_cast<QMetaObject *>(this) = *metaObject;
- propertyCache = new QQmlPropertyCache(metaObject.data(), model.modelItemRevision);
+ propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision));
}
};
@@ -659,8 +658,8 @@ public:
{
VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
if (!propertyCache) {
- dataType->propertyCache = new QQmlPropertyCache(
- &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision);
+ dataType->propertyCache.adopt(new QQmlPropertyCache(
+ &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision));
}
return new QQmlDMListAccessorData(