diff options
Diffstat (limited to 'src/qml')
115 files changed, 3001 insertions, 1478 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 2215551b95..8809221abe 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -45,6 +45,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmlcompiler_p.h> +#include <private/qqmlglobal_p.h> #include <QCoreApplication> #ifdef CONST @@ -53,6 +54,8 @@ QT_USE_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); + using namespace QtQml; #define COMPILE_EXCEPTION(location, desc) \ @@ -278,7 +281,7 @@ bool QQmlCodeGenerator::sanityCheckFunctionNames() { QSet<QString> functionNames; for (Function *f = _object->functions->first; f; f = f->next) { - AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index)); + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index).node); Q_ASSERT(function); QString name = function->name.toString(); if (functionNames.contains(name)) @@ -1201,13 +1204,19 @@ int QmlUnitGenerator::getStringId(const QString &str) const return jsUnitGenerator->getStringId(str); } -JSCodeGen::JSCodeGen(QQmlEnginePrivate *enginePrivate, const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports) +JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports) : QQmlJS::Codegen(/*strict mode*/false) - , engine(enginePrivate) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) , imports(imports) + , _disableAcceleratedLookups(false) + , _contextObject(0) + , _scopeObject(0) + , _contextObjectTemp(-1) + , _scopeObjectTemp(-1) + , _importedScriptsTemp(-1) + , _idArrayTemp(-1) { _module = jsModule; _module->setFileName(fileName); @@ -1226,23 +1235,23 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) _scopeObject = scopeObject; } -QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<AST::Node*> &functions, const QHash<int, QString> &functionNames) +QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions) { QVector<int> runtimeFunctionIndices(functions.size()); ScanFunctions scan(this, sourceCode, GlobalCode); scan.enterEnvironment(0, QmlBinding); scan.enterQmlScope(qmlRoot, QStringLiteral("context scope")); - foreach (AST::Node *node, functions) { - Q_ASSERT(node != qmlRoot); - AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); + foreach (const CompiledFunctionOrExpression &f, functions) { + Q_ASSERT(f.node != qmlRoot); + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(f.node); if (function) scan.enterQmlFunction(function); else - scan.enterEnvironment(node, QmlBinding); + scan.enterEnvironment(f.node, QmlBinding); - scan(function ? function->body : node); + scan(function ? function->body : f.node); scan.leaveEnvironment(); } scan.leaveEnvironment(); @@ -1252,7 +1261,8 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<AST::N _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); for (int i = 0; i < functions.count(); ++i) { - AST::Node *node = functions.at(i); + const CompiledFunctionOrExpression &qmlFunction = functions.at(i); + AST::Node *node = qmlFunction.node; Q_ASSERT(node != qmlRoot); AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); @@ -1260,8 +1270,10 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<AST::N QString name; if (function) name = function->name.toString(); + else if (!qmlFunction.name.isEmpty()) + name = qmlFunction.name; else - name = functionNames.value(i, QStringLiteral("%qml-expression-entry")); + name = QStringLiteral("%qml-expression-entry"); AST::SourceElements *body; if (function) @@ -1281,6 +1293,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<AST::N body = body->finish(); } + _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups; int idx = defineFunction(name, node, function ? function->formals : 0, body); @@ -1318,51 +1331,215 @@ QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache return pd; } -V4IR::Expr *JSCodeGen::member(V4IR::Expr *base, const QString *name) +static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); + +enum MetaObjectResolverFlags { + AllPropertiesAreFinal = 0x1, + LookupsIncludeEnums = 0x2, + LookupsExcludeProperties = 0x4, + ResolveTypeInformationOnly = 0x8 +}; + +static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); + +static V4IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member) { - V4IR::Member *baseAsMember = base->asMember(); - if (baseAsMember) { - QQmlPropertyCache *cache = 0; + V4IR::Type result = V4IR::VarType; + + QQmlType *type = static_cast<QQmlType*>(resolver->data); + if (type->isSingleton()) { + if (type->isCompositeSingleton()) { + QQmlTypeData *tdata = qmlEngine->typeLoader.getType(type->singletonInstanceInfo()->url); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + initMetaObjectResolver(resolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId)); + resolver->flags |= AllPropertiesAreFinal; + } else { + const QMetaObject *singletonMo = type->singletonInstanceInfo()->instanceMetaObject; + if (!singletonMo) { // We can only accelerate C++ singletons that were registered with their meta-type + resolver->clear(); + return result; + } + initMetaObjectResolver(resolver, qmlEngine->cache(singletonMo)); + resolver->flags |= LookupsIncludeEnums; + } + return resolver->resolveMember(qmlEngine, resolver, member); + } else { + if (member->name->constData()->isUpper()) { + bool ok = false; + int value = type->enumValue(*member->name, &ok); + if (ok) { + member->setEnumValue(value); + resolver->clear(); + return V4IR::SInt32Type; + } + } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType()) { + QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); + initMetaObjectResolver(resolver, cache); + member->setAttachedPropertiesId(type->attachedPropertiesId()); + return resolver->resolveMember(qmlEngine, resolver, member); + } + } - if (baseAsMember->type == V4IR::Member::MemberOfQObject - && baseAsMember->property->isQObject()) { + resolver->clear(); + return result; +} - bool propertySuitable = baseAsMember->property->isFinal(); +static void initQmlTypeResolver(V4IR::MemberExpressionResolver *resolver, QQmlType *qmlType) +{ + resolver->resolveMember = &resolveQmlType; + resolver->data = qmlType; + resolver->extraData = 0; + resolver->flags = 0; +} - if (!propertySuitable) { - // Properties of the scope or context object do not need to be final, as we - // intend to find the version of a property available at compile time, not at run-time. - if (V4IR::Name *baseName = baseAsMember->base->asName()) - propertySuitable = baseName->builtin == V4IR::Name::builtin_qml_scope_object || baseName->builtin == V4IR::Name::builtin_qml_context_object; +static V4IR::Type resolveImportNamespace(QQmlEnginePrivate *, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member) +{ + V4IR::Type result = V4IR::VarType; + QQmlTypeNameCache *typeNamespace = static_cast<QQmlTypeNameCache*>(resolver->extraData); + void *importNamespace = resolver->data; + + 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 = V4IR::VarType; + } else if (r.type) { + // 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()) { + initQmlTypeResolver(resolver, r.type); + return V4IR::QObjectType; } + } else { + Q_ASSERT(false); // How can this happen? + } + } - // Check if it's suitable for caching - if (propertySuitable) - cache = engine->propertyCacheForType(baseAsMember->property->propType); - } else if (baseAsMember->type == V4IR::Member::MemberOfQmlContext) { - // Similarly, properties of an id referenced object also don't need to be final, because - // we intend to find the version of a property available at compile time, not at run-time. - foreach (const IdMapping &mapping, _idObjects) { - if (baseAsMember->memberIndex == mapping.idIndex) { - cache = mapping.type; - break; + resolver->clear(); + return result; +} + +static void initImportNamespaceResolver(V4IR::MemberExpressionResolver *resolver, QQmlTypeNameCache *imports, const void *importNamespace) +{ + resolver->resolveMember = &resolveImportNamespace; + resolver->data = const_cast<void*>(importNamespace); + resolver->extraData = imports; + resolver->flags = 0; +} + +static V4IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member) +{ + V4IR::Type result = V4IR::VarType; + QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data); + + 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); + resolver->clear(); + return V4IR::SInt32Type; + } + } + } + + if (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 (cache) { - if (QQmlPropertyData *pd = lookupQmlCompliantProperty(cache, *name)) { - const unsigned baseTemp = _block->newTemp(); - move(_block->TEMP(baseTemp), base); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(baseTemp), name, pd); + 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 V4IR::VarType; + + switch (property->propType) { + case QMetaType::Bool: result = V4IR::BoolType; break; + case QMetaType::Int: result = V4IR::SInt32Type; break; + case QMetaType::Double: result = V4IR::DoubleType; break; + case QMetaType::QString: result = V4IR::StringType; break; + default: + if (property->isQObject()) { + if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) { + initMetaObjectResolver(resolver, cache); + return V4IR::QObjectType; + } + } else if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property->propType)) { + if (QQmlPropertyCache *cache = qmlEngine->cache(valueType->metaObject())) { + initMetaObjectResolver(resolver, cache); + resolver->flags |= ResolveTypeInformationOnly; + return V4IR::QObjectType; + } + } + break; } } } - return QQmlJS::Codegen::member(base, name); + resolver->clear(); + return result; +} + +static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject) +{ + resolver->resolveMember = &resolveMetaObjectProperty; + resolver->data = metaObject; + resolver->flags = 0; + resolver->isQObjectResolver = true; +} + +void JSCodeGen::beginFunctionBodyHook() +{ + _contextObjectTemp = _block->newTemp(); + _scopeObjectTemp = _block->newTemp(); + _importedScriptsTemp = _block->newTemp(); + _idArrayTemp = _block->newTemp(); + + V4IR::Temp *temp = _block->TEMP(_contextObjectTemp); + initMetaObjectResolver(&temp->memberResolver, _contextObject); + move(temp, _block->NAME(V4IR::Name::builtin_qml_context_object, 0, 0)); + + temp = _block->TEMP(_scopeObjectTemp); + initMetaObjectResolver(&temp->memberResolver, _scopeObject); + move(temp, _block->NAME(V4IR::Name::builtin_qml_scope_object, 0, 0)); + + move(_block->TEMP(_importedScriptsTemp), _block->NAME(V4IR::Name::builtin_qml_imported_scripts_object, 0, 0)); + move(_block->TEMP(_idArrayTemp), _block->NAME(V4IR::Name::builtin_qml_id_array, 0, 0)); } V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) { + if (_disableAcceleratedLookups) + return 0; + + Q_UNUSED(line) + Q_UNUSED(col) // Implement QML lookup semantics in the current file context. // // Note: We do not check if properties of the qml scope object or context object @@ -1378,17 +1555,42 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { _function->idObjectDependencies.insert(mapping.idIndex); - return _block->QML_CONTEXT_MEMBER(_block->NAME(V4IR::Name::builtin_qml_id_scope, line, col), - _function->newString(mapping.name), mapping.idIndex); + V4IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(V4IR::SInt32Type, mapping.idIndex)); + V4IR::Temp *result = _block->TEMP(_block->newTemp()); + _block->MOVE(result, s); + result = _block->TEMP(result->index); + initMetaObjectResolver(&result->memberResolver, mapping.type); + result->memberResolver.flags |= AllPropertiesAreFinal; + result->isReadOnly = true; // don't allow use as lvalue + return result; } { QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { - if (r.scriptIndex != -1) - return subscript(_block->NAME(V4IR::Name::builtin_qml_imported_scripts_object, line, col), _block->CONST(V4IR::NumberType, r.scriptIndex)); - else - return 0; // TODO: We can't do fast lookup for these yet. + if (r.scriptIndex != -1) { + return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(V4IR::SInt32Type, r.scriptIndex)); + } else if (r.type) { + V4IR::Name *typeName = _block->NAME(name, line, col); + // Make sure the run-time loads this through the more efficient singleton getter. + typeName->qmlSingleton = r.type->isSingleton(); + typeName->freeOfSideEffects = true; + + V4IR::Temp *result = _block->TEMP(_block->newTemp()); + initQmlTypeResolver(&result->memberResolver, r.type); + + _block->MOVE(result, typeName); + return _block->TEMP(result->index); + } else { + Q_ASSERT(r.importNamespace); + V4IR::Name *namespaceName = _block->NAME(name, line, col); + namespaceName->freeOfSideEffects = true; + V4IR::Temp *result = _block->TEMP(_block->newTemp()); + initImportNamespaceResolver(&result->memberResolver, imports, r.importNamespace); + + _block->MOVE(result, namespaceName); + return _block->TEMP(result->index); + } } } @@ -1398,11 +1600,9 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col if (propertyExistsButForceNameLookup) return 0; if (pd) { - if (!pd->isConstant()) - _function->scopeObjectDependencies.insert(pd); - int base = _block->newTemp(); - move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_scope_object, line, col)); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), _function->newString(name), pd); + V4IR::Temp *base = _block->TEMP(_scopeObjectTemp); + initMetaObjectResolver(&base->memberResolver, _scopeObject); + return _block->MEMBER(base, _function->newString(name), pd, V4IR::Member::MemberOfQmlScopeObject); } } @@ -1412,11 +1612,9 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col if (propertyExistsButForceNameLookup) return 0; if (pd) { - if (!pd->isConstant()) - _function->contextObjectDependencies.insert(pd); - int base = _block->newTemp(); - move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_context_object, line, col)); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), _function->newString(name), pd); + V4IR::Temp *base = _block->TEMP(_contextObjectTemp); + initMetaObjectResolver(&base->memberResolver, _contextObject); + return _block->MEMBER(base, _function->newString(name), pd, V4IR::Member::MemberOfQmlContextObject); } } @@ -1556,7 +1754,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (paramList) paramList = paramList->finish(); - AST::Statement *statement = static_cast<AST::Statement*>(parsedQML->functions[binding->value.compiledScriptIndex]); + AST::Statement *statement = static_cast<AST::Statement*>(parsedQML->functions[binding->value.compiledScriptIndex].node); AST::SourceElement *sourceElement = new (pool) AST::StatementSourceElement(statement); AST::SourceElements *elements = new (pool) AST::SourceElements(sourceElement); elements = elements->finish(); diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index a5fec65111..0a0e4f2d5b 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -166,6 +166,20 @@ struct Pragma QV4::CompiledData::Location location; }; +struct CompiledFunctionOrExpression +{ + CompiledFunctionOrExpression() + : disableAcceleratedLookups(false) + {} + CompiledFunctionOrExpression(AST::Node *n) + : node(n) + , disableAcceleratedLookups(false) + {} + AST::Node *node; // FunctionDeclaration, Statement or Expression + QString name; + bool disableAcceleratedLookups; +}; + struct ParsedQML { ParsedQML(bool debugMode) @@ -180,7 +194,7 @@ struct ParsedQML AST::UiProgram *program; int indexOfRootObject; QList<QmlObject*> objects; - QList<AST::Node*> functions; // FunctionDeclaration, Statement or Expression + QList<CompiledFunctionOrExpression> functions; QV4::Compiler::JSUnitGenerator jsGenerator; QV4::CompiledData::TypeReferenceMap typeReferences; @@ -269,7 +283,7 @@ public: QList<QV4::CompiledData::Import*> _imports; QList<Pragma*> _pragmas; QList<QmlObject*> _objects; - QList<AST::Node*> _functions; + QList<CompiledFunctionOrExpression> _functions; QV4::CompiledData::TypeReferenceMap _typeReferences; @@ -347,7 +361,7 @@ private: struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen { - JSCodeGen(QQmlEnginePrivate *enginePrivate, const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, + JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, QQmlJS::Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports); struct IdMapping @@ -362,26 +376,28 @@ struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen void beginObjectScope(QQmlPropertyCache *scopeObject); // Returns mapping from input functions to index in V4IR::Module::functions / compiledData->runtimeFunctions - QVector<int> generateJSCodeForFunctionsAndBindings(const QList<AST::Node*> &functions, const QHash<int, QString> &functionNames); - - // Resolve QObject members with the help of QQmlEngine's meta type registry - virtual V4IR::Expr *member(V4IR::Expr *base, const QString *name); + QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); protected: + virtual void beginFunctionBodyHook(); virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); private: QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup = 0); - QQmlEnginePrivate *engine; QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool AST::UiProgram *qmlRoot; QQmlTypeNameCache *imports; + bool _disableAcceleratedLookups; ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; + int _contextObjectTemp; + int _scopeObjectTemp; + int _importedScriptsTemp; + int _idArrayTemp; }; } // namespace QtQml diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 893abc9659..a920f1b419 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1632,7 +1632,7 @@ bool Codegen::visit(ObjectLiteral *ast) if (!valueMap.isEmpty()) { V4IR::ExprList *current; for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { - if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) { + if (QV4::String::toArrayIndex(it.key()) != UINT_MAX) { ++it; continue; } @@ -2050,6 +2050,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); } + beginFunctionBodyHook(); + sourceElements(body); _function->insertBasicBlock(_exitBlock); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index de22e8904b..32f1f1bfd4 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -293,7 +293,7 @@ protected: _exceptionHandlers.pop(); } - virtual V4IR::Expr *member(V4IR::Expr *base, const QString *name); // Re-implemented by QML to resolve QObject property members + V4IR::Expr *member(V4IR::Expr *base, const QString *name); V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); V4IR::Expr *argument(V4IR::Expr *expr); V4IR::Expr *reference(V4IR::Expr *expr); @@ -330,6 +330,7 @@ protected: V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); // Hook provided to implement QML lookup semantics virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); + virtual void beginFunctionBodyHook() {} // nodes virtual bool visit(AST::ArgumentList *ast); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index cb17b86702..9041b04837 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -170,20 +170,6 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total registerString(*f->formals.at(i)); for (int i = 0; i < f->locals.size(); ++i) registerString(*f->locals.at(i)); - - if (f->hasQmlDependencies()) { - QSet<int> idObjectDeps = f->idObjectDependencies; - QSet<QQmlPropertyData*> contextPropertyDeps = f->contextObjectDependencies; - QSet<QQmlPropertyData*> scopePropertyDeps = f->scopeObjectDependencies; - - if (!idObjectDeps.isEmpty()) - qmlIdObjectDependenciesPerFunction.insert(f, idObjectDeps); - if (!contextPropertyDeps.isEmpty()) - qmlContextPropertyDependenciesPerFunction.insert(f, contextPropertyDeps); - if (!scopePropertyDeps.isEmpty()) - qmlScopePropertyDependenciesPerFunction.insert(f, scopePropertyDeps); - } - } int unitSize = QV4::CompiledData::Unit::calculateSize(headerSize, strings.size(), irModule->functions.size(), regexps.size(), @@ -199,23 +185,8 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *total if (lineNumberMapping != lineNumberMappingsPerFunction.constEnd()) lineNumberMappingCount = lineNumberMapping->count() / 2; - int qmlIdDepsCount = 0; - int qmlPropertyDepsCount = 0; - - if (f->hasQmlDependencies()) { - IdDependencyHash::ConstIterator idIt = qmlIdObjectDependenciesPerFunction.find(f); - if (idIt != qmlIdObjectDependenciesPerFunction.constEnd()) - qmlIdDepsCount += idIt->count(); - - PropertyDependencyHash::ConstIterator it = qmlContextPropertyDependenciesPerFunction.find(f); - if (it != qmlContextPropertyDependenciesPerFunction.constEnd()) - qmlPropertyDepsCount += it->count(); - - it = qmlScopePropertyDependenciesPerFunction.find(f); - if (it != qmlScopePropertyDependenciesPerFunction.constEnd()) - qmlPropertyDepsCount += it->count(); - } - + const int qmlIdDepsCount = f->idObjectDependencies.count(); + const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount, qmlIdDepsCount, qmlPropertyDepsCount); } @@ -353,32 +324,22 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 function->nDependingContextProperties = 0; function->nDependingScopeProperties = 0; - QSet<int> qmlIdObjectDeps; - QSet<QQmlPropertyData*> qmlContextPropertyDeps; - QSet<QQmlPropertyData*> qmlScopePropertyDeps; - - if (irFunction->hasQmlDependencies()) { - qmlIdObjectDeps = qmlIdObjectDependenciesPerFunction.value(irFunction); - qmlContextPropertyDeps = qmlContextPropertyDependenciesPerFunction.value(irFunction); - qmlScopePropertyDeps = qmlScopePropertyDependenciesPerFunction.value(irFunction); - - if (!qmlIdObjectDeps.isEmpty()) { - function->nDependingIdObjects = qmlIdObjectDeps.count(); - function->dependingIdObjectsOffset = currentOffset; - currentOffset += function->nDependingIdObjects * sizeof(quint32); - } - - if (!qmlContextPropertyDeps.isEmpty()) { - function->nDependingContextProperties = qmlContextPropertyDeps.count(); - function->dependingContextPropertiesOffset = currentOffset; - currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2; - } - - if (!qmlScopePropertyDeps.isEmpty()) { - function->nDependingScopeProperties = qmlScopePropertyDeps.count(); - function->dependingScopePropertiesOffset = currentOffset; - currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2; - } + if (!irFunction->idObjectDependencies.isEmpty()) { + function->nDependingIdObjects = irFunction->idObjectDependencies.count(); + function->dependingIdObjectsOffset = currentOffset; + currentOffset += function->nDependingIdObjects * sizeof(quint32); + } + + if (!irFunction->contextObjectPropertyDependencies.isEmpty()) { + function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count(); + function->dependingContextPropertiesOffset = currentOffset; + currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2; + } + + if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) { + function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count(); + function->dependingScopePropertiesOffset = currentOffset; + currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2; } function->location.line = irFunction->line; @@ -407,19 +368,21 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 // write QML dependencies quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); - foreach (int id, qmlIdObjectDeps) + foreach (int id, irFunction->idObjectDependencies) *writtenDeps++ = id; writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset); - foreach (QQmlPropertyData *property, qmlContextPropertyDeps) { - *writtenDeps++ = property->coreIndex; - *writtenDeps++ = property->notifyIndex; + for (QQmlJS::V4IR::PropertyDependencyMap::ConstIterator property = irFunction->contextObjectPropertyDependencies.constBegin(), end = irFunction->contextObjectPropertyDependencies.constEnd(); + property != end; ++property) { + *writtenDeps++ = property.key(); // property index + *writtenDeps++ = property.value(); // notify index } writtenDeps = (quint32 *)(f + function->dependingScopePropertiesOffset); - foreach (QQmlPropertyData *property, qmlScopePropertyDeps) { - *writtenDeps++ = property->coreIndex; - *writtenDeps++ = property->notifyIndex; + for (QQmlJS::V4IR::PropertyDependencyMap::ConstIterator property = irFunction->scopeObjectPropertyDependencies.constBegin(), end = irFunction->scopeObjectPropertyDependencies.constEnd(); + property != end; ++property) { + *writtenDeps++ = property.key(); // property index + *writtenDeps++ = property.value(); // notify index } return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions, function->nLineNumberMappingEntries, diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 6338babb5a..1596fcb622 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -94,13 +94,6 @@ struct Q_QML_EXPORT JSUnitGenerator { QList<QList<CompiledData::JSClassMember> > jsClasses; uint jsClassDataSize; uint headerSize; - - typedef QHash<QQmlJS::V4IR::Function *, QSet<int> > IdDependencyHash; - IdDependencyHash qmlIdObjectDependenciesPerFunction; - - typedef QHash<QQmlJS::V4IR::Function *, QSet<QQmlPropertyData*> > PropertyDependencyHash; - PropertyDependencyHash qmlContextPropertyDependenciesPerFunction; - PropertyDependencyHash qmlScopePropertyDependenciesPerFunction; }; } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 9baf7f89ca..5b5667abb4 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -67,6 +67,7 @@ QT_BEGIN_NAMESPACE F(SetLookup, setLookup) \ F(StoreQObjectProperty, storeQObjectProperty) \ F(LoadQObjectProperty, loadQObjectProperty) \ + F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ @@ -98,6 +99,7 @@ QT_BEGIN_NAMESPACE F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ + F(ConstructPropertyLookup, constructPropertyLookup) \ F(CreateActivationProperty, createActivationProperty) \ F(ConstructGlobalLookup, constructGlobalLookup) \ F(Jump, jump) \ @@ -125,10 +127,11 @@ QT_BEGIN_NAMESPACE F(MulNumberParams, mulNumberParams) \ F(SubNumberParams, subNumberParams) \ F(LoadThis, loadThis) \ - F(LoadQmlIdObject, loadQmlIdObject) \ + F(LoadQmlIdArray, loadQmlIdArray) \ F(LoadQmlImportedScripts, loadQmlImportedScripts) \ F(LoadQmlContextObject, loadQmlContextObject) \ - F(LoadQmlScopeObject, loadQmlScopeObject) + F(LoadQmlScopeObject, loadQmlScopeObject) \ + F(LoadQmlSingleton, loadQmlSingleton) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -286,8 +289,15 @@ union Instr int propertyIndex; Param base; Param result; + int attachedPropertiesId; bool captureRequired; }; + struct instr_loadAttachedQObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param result; + int attachedPropertiesId; + }; struct instr_storeProperty { MOTH_INSTR_HEADER int name; @@ -491,6 +501,14 @@ union Instr Param base; Param result; }; + struct instr_constructPropertyLookup { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; struct instr_createActivationProperty { MOTH_INSTR_HEADER int name; @@ -645,10 +663,9 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlIdObject { + struct instr_loadQmlIdArray { MOTH_INSTR_HEADER Param result; - int id; }; struct instr_loadQmlImportedScripts { MOTH_INSTR_HEADER @@ -662,6 +679,11 @@ union Instr MOTH_INSTR_HEADER Param result; }; + struct instr_loadQmlSingleton { + MOTH_INSTR_HEADER + Param result; + int name; + }; instr_common common; instr_ret ret; @@ -678,6 +700,7 @@ union Instr instr_loadProperty loadProperty; instr_getLookup getLookup; instr_loadQObjectProperty loadQObjectProperty; + instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; instr_storeProperty storeProperty; instr_setLookup setLookup; instr_storeQObjectProperty storeQObjectProperty; @@ -712,6 +735,7 @@ union Instr instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject; instr_createValue createValue; instr_createProperty createProperty; + instr_constructPropertyLookup constructPropertyLookup; instr_createActivationProperty createActivationProperty; instr_constructGlobalLookup constructGlobalLookup; instr_jump jump; @@ -739,10 +763,11 @@ union Instr instr_mulNumberParams mulNumberParams; instr_subNumberParams subNumberParams; instr_loadThis loadThis; - instr_loadQmlIdObject loadQmlIdObject; + instr_loadQmlIdArray loadQmlIdArray; instr_loadQmlImportedScripts loadQmlImportedScripts; instr_loadQmlContextObject loadQmlContextObject; instr_loadQmlScopeObject loadQmlScopeObject; + instr_loadQmlSingleton loadQmlSingleton; static int size(Type type); }; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index a999dd4da1..35097bae49 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -68,7 +68,7 @@ using namespace QV4; CompilationUnit::~CompilationUnit() { foreach (Function *f, runtimeFunctions) - engine->allFunctions.remove(reinterpret_cast<quintptr>(f->codePtr)); + engine->allFunctions.remove(reinterpret_cast<quintptr>(f->code)); } void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) @@ -85,7 +85,7 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } foreach (Function *f, runtimeFunctions) - engine->allFunctions.insert(reinterpret_cast<quintptr>(f->codePtr), f); + engine->allFunctions.insert(reinterpret_cast<quintptr>(f->code), f); } QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int functionIndex) @@ -439,15 +439,6 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi } #endif -void Assembler::recordLineNumber(int lineNumber) -{ - CodeLineNumerMapping mapping; - mapping.location = label(); - mapping.lineNumber = lineNumber; - codeLineNumberMappings << mapping; -} - - JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) { Label endOfCode = label(); @@ -467,14 +458,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) JSC::JSGlobalData dummy(_executableAllocator); JSC::LinkBuffer linkBuffer(dummy, this, 0); - QVector<uint> lineNumberMapping(codeLineNumberMappings.count() * 2); - - for (int i = 0; i < codeLineNumberMappings.count(); ++i) { - lineNumberMapping[i * 2] = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); - lineNumberMapping[i * 2 + 1] = codeLineNumberMappings.at(i).lineNumber; - } - _isel->jsUnitGenerator()->registerLineNumberMapping(_function, lineNumberMapping); - QHash<void*, const char*> functions; foreach (CallToLink ctl, _callsToLink) { linkBuffer.link(ctl.call, ctl.externalFunction); @@ -556,10 +539,11 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) : EvalInstructionSelection(execAllocator, module, jsGenerator) , _block(0) , _as(0) + , qmlEngine(qmlEngine) { compilationUnit = new CompilationUnit; compilationUnit->codeRefs.resize(module->functions.size()); @@ -578,7 +562,7 @@ void InstructionSelection::run(int functionIndex) qSwap(_function, function); V4IR::Optimizer opt(_function); - opt.run(); + opt.run(qmlEngine); #if (CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX))) || (CPU(X86) && OS(LINUX)) static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty(); @@ -642,9 +626,9 @@ void InstructionSelection::run(int functionIndex) foreach (V4IR::Stmt *s, _block->statements) { if (s->location.isValid()) { - _as->recordLineNumber(s->location.startLine); if (int(s->location.startLine) != lastLine) { - _as->saveInstructionPointer(Assembler::ScratchRegister); + Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lineNumber)); + _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } } @@ -896,9 +880,9 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) #endif } -void InstructionSelection::loadQmlIdObject(int id, V4IR::Temp *temp) +void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp) { - generateFunctionCall(temp, __qmljs_get_id_object, Assembler::ContextRegister, Assembler::TrustedImm32(id)); + generateFunctionCall(temp, __qmljs_get_id_array, Assembler::ContextRegister); } void InstructionSelection::loadQmlImportedScripts(V4IR::Temp *temp) @@ -916,6 +900,11 @@ void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) generateFunctionCall(temp, __qmljs_get_scope_object, Assembler::ContextRegister); } +void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_qml_singleton, Assembler::ContextRegister, Assembler::PointerToString(name)); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { if (targetTemp->kind == V4IR::Temp::PhysicalRegister) { @@ -994,10 +983,13 @@ void InstructionSelection::getProperty(V4IR::Expr *base, const QString &name, V4 } } -void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *target) +void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *target) { - generateFunctionCall(target, __qmljs_get_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + if (attachedPropertiesId != 0) + generateFunctionCall(target, __qmljs_get_attached_property, Assembler::ContextRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); + else + generateFunctionCall(target, __qmljs_get_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), + Assembler::TrustedImm32(captureRequired)); } void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, @@ -1786,9 +1778,18 @@ void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::E void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) { - prepareCallData(args, 0); + prepareCallData(args, base); + if (useFastLookups) { + uint index = registerGetterLookup(name); + generateFunctionCall(result, __qmljs_construct_property_lookup, + Assembler::ContextRegister, + Assembler::TrustedImm32(index), + baseAddressForCallData()); + return; + } + generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, - Assembler::Reference(base), Assembler::PointerToString(name), + Assembler::PointerToString(name), baseAddressForCallData()); } @@ -2493,11 +2494,10 @@ bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource, else targetReg = Assembler::ReturnValueRegister; - _as->move(_as->toInt32Register(leftSource, targetReg), targetReg); - Assembler::RegisterID rReg = _as->toInt32Register(rightSource, Assembler::ScratchRegister); - _as->and32(rReg, targetReg); - if (Assembler::ReturnValueRegister == targetReg) - _as->storeInt32(targetReg, target); + _as->and32(_as->toInt32Register(leftSource, targetReg), + _as->toInt32Register(rightSource, Assembler::ScratchRegister), + targetReg); + _as->storeInt32(targetReg, target); } return true; case V4IR::OpBitOr: { Q_ASSERT(rightSource->type == V4IR::SInt32Type); @@ -2514,11 +2514,10 @@ bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource, else targetReg = Assembler::ReturnValueRegister; - _as->move(_as->toInt32Register(leftSource, targetReg), targetReg); - Assembler::RegisterID rReg = _as->toInt32Register(rightSource, Assembler::ScratchRegister); - _as->or32(rReg, targetReg); - if (Assembler::ReturnValueRegister == targetReg) - _as->storeInt32(targetReg, target); + _as->or32(_as->toInt32Register(leftSource, targetReg), + _as->toInt32Register(rightSource, Assembler::ScratchRegister), + targetReg); + _as->storeInt32(targetReg, target); } return true; case V4IR::OpBitXor: { Q_ASSERT(rightSource->type == V4IR::SInt32Type); @@ -2535,32 +2534,41 @@ bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource, else targetReg = Assembler::ReturnValueRegister; - _as->move(_as->toInt32Register(leftSource, targetReg), targetReg); - Assembler::RegisterID rReg = _as->toInt32Register(rightSource, Assembler::ScratchRegister); - _as->xor32(rReg, targetReg); - if (Assembler::ReturnValueRegister == targetReg) - _as->storeInt32(targetReg, target); + _as->xor32(_as->toInt32Register(leftSource, targetReg), + _as->toInt32Register(rightSource, Assembler::ScratchRegister), + targetReg); + _as->storeInt32(targetReg, target); } return true; - case V4IR::OpLShift: + case V4IR::OpLShift: { Q_ASSERT(rightSource->type == V4IR::SInt32Type); - _as->move(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister), - Assembler::ReturnValueRegister); + Assembler::RegisterID targetReg; + if (target->kind == V4IR::Temp::PhysicalRegister) + targetReg = (Assembler::RegisterID) target->index; + else + targetReg = Assembler::ReturnValueRegister; + _as->move(_as->toInt32Register(rightSource, Assembler::ScratchRegister), Assembler::ScratchRegister); _as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); // TODO: for constants, do this in the IR - _as->lshift32(Assembler::ScratchRegister, Assembler::ReturnValueRegister); + _as->lshift32(_as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, + Assembler::ReturnValueRegister); _as->storeInt32(Assembler::ReturnValueRegister, target); - return true; - case V4IR::OpRShift: + } return true; + case V4IR::OpRShift: { Q_ASSERT(rightSource->type == V4IR::SInt32Type); - _as->move(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister), - Assembler::ReturnValueRegister); + Assembler::RegisterID targetReg; + if (target->kind == V4IR::Temp::PhysicalRegister) + targetReg = (Assembler::RegisterID) target->index; + else + targetReg = Assembler::ReturnValueRegister; + _as->move(_as->toInt32Register(rightSource, Assembler::ScratchRegister), Assembler::ScratchRegister); _as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); // TODO: for constants, do this in the IR - _as->rshift32(Assembler::ScratchRegister, Assembler::ReturnValueRegister); + _as->rshift32(_as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, + Assembler::ReturnValueRegister); _as->storeInt32(Assembler::ReturnValueRegister, target); - return true; + } return true; case V4IR::OpURShift: Q_ASSERT(rightSource->type == V4IR::SInt32Type); _as->move(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister), @@ -2571,6 +2579,58 @@ bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource, _as->urshift32(Assembler::ScratchRegister, Assembler::ReturnValueRegister); _as->storeUInt32(Assembler::ReturnValueRegister, target); return true; + case V4IR::OpAdd: { + Q_ASSERT(rightSource->type == V4IR::SInt32Type); + + Assembler::RegisterID targetReg; + if (target->kind == V4IR::Temp::PhysicalRegister) + targetReg = (Assembler::RegisterID) target->index; + else + targetReg = Assembler::ReturnValueRegister; + + _as->add32(_as->toInt32Register(leftSource, targetReg), + _as->toInt32Register(rightSource, Assembler::ScratchRegister), + targetReg); + _as->storeInt32(targetReg, target); + } return true; + case V4IR::OpSub: { + Q_ASSERT(rightSource->type == V4IR::SInt32Type); + + if (rightSource->asTemp() && rightSource->asTemp()->kind == V4IR::Temp::PhysicalRegister + && target->kind == V4IR::Temp::PhysicalRegister + && target->index == rightSource->asTemp()->index) { + Assembler::RegisterID targetReg = (Assembler::RegisterID) target->index; + _as->move(targetReg, Assembler::ScratchRegister); + _as->move(_as->toInt32Register(leftSource, targetReg), targetReg); + _as->sub32(Assembler::ScratchRegister, targetReg); + _as->storeInt32(targetReg, target); + return true; + } + + Assembler::RegisterID targetReg; + if (target->kind == V4IR::Temp::PhysicalRegister) + targetReg = (Assembler::RegisterID) target->index; + else + targetReg = Assembler::ReturnValueRegister; + + _as->move(_as->toInt32Register(leftSource, targetReg), targetReg); + _as->sub32(_as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg); + _as->storeInt32(targetReg, target); + } return true; + case V4IR::OpMul: { + Q_ASSERT(rightSource->type == V4IR::SInt32Type); + + Assembler::RegisterID targetReg; + if (target->kind == V4IR::Temp::PhysicalRegister) + targetReg = (Assembler::RegisterID) target->index; + else + targetReg = Assembler::ReturnValueRegister; + + _as->mul32(_as->toInt32Register(leftSource, targetReg), + _as->toInt32Register(rightSource, Assembler::ScratchRegister), + targetReg); + _as->storeInt32(targetReg, target); + } return true; default: return false; } diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index e3b41857ea..f5d4e469e5 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -466,23 +466,6 @@ public: V4IR::BasicBlock *block; }; - void saveInstructionPointer(RegisterID freeScratchRegister) { - Address ipAddr(ContextRegister, qOffsetOf(QV4::ExecutionContext, jitInstructionPointer)); - RegisterID sourceRegister = freeScratchRegister; - -#if CPU(X86_64) || CPU(X86) - callToRetrieveIP(); - peek(sourceRegister); - pop(); -#elif CPU(ARM) - move(JSC::ARMRegisters::pc, sourceRegister); -#else -#error "Port me!" -#endif - - storePtr(sourceRegister, ipAddr); - } - void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; ctl.call = call(); @@ -1056,7 +1039,6 @@ public: static const BinaryOperationInfo &binaryOperation(V4IR::AluOp operation) { return binaryOperations[operation]; } - Jump inline_add32(Address addr, RegisterID reg) { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) @@ -1397,8 +1379,6 @@ public: JSC::MacroAssemblerCodeRef link(int *codeSize); - void recordLineNumber(int lineNumber); - const StackLayout stackLayout() const { return _stackLayout; } ConstantTable &constantTable() { return _constTable; } @@ -1424,13 +1404,6 @@ private: QV4::ExecutableAllocator *_executableAllocator; InstructionSelection *_isel; - - struct CodeLineNumerMapping - { - Assembler::Label location; - int lineNumber; - }; - QVector<CodeLineNumerMapping> codeLineNumberMappings; }; template <typename T> inline void prepareRelativeCall(const T &, Assembler *){} @@ -1445,7 +1418,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -1483,10 +1456,11 @@ protected: virtual void callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4IR::ExprList *args, V4IR::Temp *result); virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadQmlIdObject(int id, V4IR::Temp *temp); + virtual void loadQmlIdArray(V4IR::Temp *temp); virtual void loadQmlImportedScripts(V4IR::Temp *temp); virtual void loadQmlContextObject(V4IR::Temp *temp); virtual void loadQmlScopeObject(V4IR::Temp *temp); + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); @@ -1496,7 +1470,7 @@ protected: virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target); virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName); virtual void setQObjectProperty(V4IR::Expr *source, V4IR::Expr *targetBase, int propertyIndex); - virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *target); + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *target); virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target); virtual void setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex); virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); @@ -1655,14 +1629,15 @@ private: Assembler* _as; CompilationUnit *compilationUnit; + QQmlEnginePrivate *qmlEngine; }; class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(execAllocator, module, jsGenerator); } + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return true; } }; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 6db8f11a9e..bf9af178fe 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -152,8 +152,9 @@ inline bool isBoolType(V4IR::Expr *e) } // anonymous namespace -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) : EvalInstructionSelection(execAllocator, module, jsGenerator) + , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) , _codeNext(0) @@ -191,7 +192,7 @@ void InstructionSelection::run(int functionIndex) qSwap(codeEnd, _codeEnd); V4IR::Optimizer opt(_function); - opt.run(); + opt.run(qmlEngine); if (opt.isInSSA()) { opt.convertOutOfSSA(); opt.showMeTheCode(_function); @@ -351,6 +352,16 @@ void InstructionSelection::constructActivationProperty(V4IR::Name *func, void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) { + if (useFastLookups) { + Instruction::ConstructPropertyLookup call; + call.base = getParam(base); + call.index = registerGetterLookup(name); + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + return; + } Instruction::CreateProperty create; create.base = getParam(base); create.name = registerString(name); @@ -377,11 +388,10 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) addInstruction(load); } -void InstructionSelection::loadQmlIdObject(int id, V4IR::Temp *temp) +void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp) { - Instruction::LoadQmlIdObject load; + Instruction::LoadQmlIdArray load; load.result = getResultParam(temp); - load.id = id; addInstruction(load); } @@ -406,6 +416,14 @@ void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) addInstruction(load); } +void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp) +{ + Instruction::LoadQmlSingleton load; + load.result = getResultParam(temp); + load.name = registerString(name); + addInstruction(load); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { assert(sourceConst); @@ -508,14 +526,22 @@ void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *ta addInstruction(store); } -void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *target) +void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *target) { - Instruction::LoadQObjectProperty load; - load.base = getParam(base); - load.propertyIndex = propertyIndex; - load.result = getResultParam(target); - load.captureRequired = captureRequired; - addInstruction(load); + if (attachedPropertiesId != 0) { + Instruction::LoadAttachedQObjectProperty load; + load.propertyIndex = propertyIndex; + load.result = getResultParam(target); + load.attachedPropertiesId = attachedPropertiesId; + addInstruction(load); + } else { + Instruction::LoadQObjectProperty load; + load.base = getParam(base); + load.propertyIndex = propertyIndex; + load.result = getResultParam(target); + load.captureRequired = captureRequired; + addInstruction(load); + } } void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index ffb8ff4539..d8a85ff249 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -68,7 +68,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -112,10 +112,11 @@ protected: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadQmlIdObject(int id, V4IR::Temp *temp); + virtual void loadQmlIdArray(V4IR::Temp *temp); virtual void loadQmlImportedScripts(V4IR::Temp *temp); virtual void loadQmlContextObject(V4IR::Temp *temp); virtual void loadQmlScopeObject(V4IR::Temp *temp); + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); @@ -125,7 +126,7 @@ protected: virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target); virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName); virtual void setQObjectProperty(V4IR::Expr *source, V4IR::Expr *targetBase, int propertyIndex); - virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *target); + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *target); virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target); virtual void setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex); virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); @@ -168,6 +169,8 @@ private: void patchJumpAddresses(); QByteArray squeezeCode() const; + QQmlEnginePrivate *qmlEngine; + V4IR::BasicBlock *_block; V4IR::BasicBlock *_nextBlock; @@ -189,8 +192,8 @@ class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(execAllocator, module, jsGenerator); } + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return false; } }; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 45b1e9f3b0..b86837e167 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -103,12 +103,16 @@ void IRDecoder::visitMove(V4IR::Move *s) if (V4IR::Name *n = s->source->asName()) { if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. loadThisObject(t); + else if (n->builtin == V4IR::Name::builtin_qml_id_array) + loadQmlIdArray(t); else if (n->builtin == V4IR::Name::builtin_qml_context_object) loadQmlContextObject(t); else if (n->builtin == V4IR::Name::builtin_qml_scope_object) loadQmlScopeObject(t); else if (n->builtin == V4IR::Name::builtin_qml_imported_scripts_object) loadQmlImportedScripts(t); + else if (n->qmlSingleton) + loadQmlSingleton(*n->id, t); else getActivationProperty(n, t); return; @@ -142,21 +146,22 @@ void IRDecoder::visitMove(V4IR::Move *s) return; } } else if (V4IR::Member *m = s->source->asMember()) { - if (m->type == V4IR::Member::MemberOfQmlContext) { - V4IR::Name *base = m->base->asName(); - Q_ASSERT(base); - - if (base->builtin == V4IR::Name::builtin_qml_id_scope) { - loadQmlIdObject(m->memberIndex, t); - return; - } - } else if (m->type == V4IR::Member::MemberOfQObject) { + if (m->property) { bool captureRequired = true; - if (_function) { - captureRequired = !_function->contextObjectDependencies.contains(m->property) - && !_function->scopeObjectDependencies.contains(m->property); + + Q_ASSERT(m->kind != V4IR::Member::MemberOfEnum); + const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + + if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) { + if (m->kind == V4IR::Member::MemberOfQmlContextObject) { + _function->contextObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + captureRequired = false; + } else if (m->kind == V4IR::Member::MemberOfQmlScopeObject) { + _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex); + captureRequired = false; + } } - getQObjectProperty(m->base, m->property->coreIndex, captureRequired, t); + getQObjectProperty(m->base, m->property->coreIndex, captureRequired, attachedPropertiesId, t); return; } else if (m->base->asTemp() || m->base->asConst()) { getProperty(m->base, *m->name, t); @@ -195,7 +200,9 @@ void IRDecoder::visitMove(V4IR::Move *s) } else if (V4IR::Member *m = s->target->asMember()) { if (m->base->asTemp() || m->base->asConst()) { if (s->source->asTemp() || s->source->asConst()) { - if (m->type == V4IR::Member::MemberOfQObject) { + Q_ASSERT(m->kind != V4IR::Member::MemberOfEnum); + const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + if (m->property && attachedPropertiesId == 0) { setQObjectProperty(s->source, m->base, m->property->coreIndex); return; } else { @@ -234,9 +241,7 @@ void IRDecoder::visitExp(V4IR::Exp *s) Q_ASSERT(member->base->asTemp()); callProperty(member->base->asTemp(), *member->name, c->args, 0); } else if (Subscript *s = c->base->asSubscript()) { - Q_ASSERT(s->base->asTemp()); - Q_ASSERT(s->index->asTemp()); - callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0); + callSubscript(s->base, s->index, c->args, 0); } else { Q_UNIMPLEMENTED(); } diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 23ef7cc69e..5ddafc07ab 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE +class QQmlEnginePrivate; + namespace QV4 { class ExecutableAllocator; struct Function; @@ -92,7 +94,7 @@ class Q_QML_EXPORT EvalISelFactory { public: virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; }; @@ -142,10 +144,11 @@ public: // to implement by subclasses: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void loadThisObject(V4IR::Temp *temp) = 0; - virtual void loadQmlIdObject(int id, V4IR::Temp *temp) = 0; + virtual void loadQmlIdArray(V4IR::Temp *temp) = 0; virtual void loadQmlImportedScripts(V4IR::Temp *temp) = 0; virtual void loadQmlContextObject(V4IR::Temp *temp) = 0; virtual void loadQmlScopeObject(V4IR::Temp *temp) = 0; + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp) = 0; virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; @@ -153,7 +156,7 @@ public: // to implement by subclasses: virtual void setActivationProperty(V4IR::Expr *source, const QString &targetName) = 0; virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; virtual void getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target) = 0; - virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, V4IR::Temp *targetTemp) = 0; + virtual void getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *targetTemp) = 0; virtual void setProperty(V4IR::Expr *source, V4IR::Expr *targetBase, const QString &targetName) = 0; virtual void setQObjectProperty(V4IR::Expr *source, V4IR::Expr *targetBase, int propertyIndex) = 0; virtual void getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 75261b2469..deb1af51b4 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -72,6 +72,7 @@ QString typeName(Type t) case NumberType: return QStringLiteral("number"); case StringType: return QStringLiteral("string"); case VarType: return QStringLiteral("var"); + case QObjectType: return QStringLiteral("qobject"); default: return QStringLiteral("multiple"); } } @@ -274,8 +275,16 @@ static QString dumpStart(const Expr *e) { if (e->type == UnknownType) // return QStringLiteral("**UNKNOWN**"); return QString(); - else - return typeName(e->type) + QStringLiteral("{"); + + QString result = typeName(e->type); + const Temp *temp = const_cast<Expr*>(e)->asTemp(); + if (e->type == QObjectType && temp && temp->memberResolver.isQObjectResolver) { + result += QLatin1Char('<'); + result += QString::fromUtf8(static_cast<QQmlPropertyCache*>(temp->memberResolver.data)->className()); + result += QLatin1Char('>'); + } + result += QLatin1Char('{'); + return result; } static const char *dumpEnd(const Expr *e) { @@ -363,6 +372,8 @@ void Name::initGlobal(const QString *id, quint32 line, quint32 column) this->id = id; this->builtin = builtin_invalid; this->global = true; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -372,6 +383,8 @@ void Name::init(const QString *id, quint32 line, quint32 column) this->id = id; this->builtin = builtin_invalid; this->global = false; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -381,6 +394,8 @@ void Name::init(Builtin builtin, quint32 line, quint32 column) this->id = 0; this->builtin = builtin; this->global = false; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -424,8 +439,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_setup_argument_object"; case V4IR::Name::builtin_convert_this_to_object: return "builtin_convert_this_to_object"; - case V4IR::Name::builtin_qml_id_scope: - return "builtin_qml_id_scope"; + case V4IR::Name::builtin_qml_id_array: + return "builtin_qml_id_array"; case V4IR::Name::builtin_qml_imported_scripts_object: return "builtin_qml_imported_scripts_object"; case V4IR::Name::builtin_qml_scope_object: @@ -539,9 +554,12 @@ void Subscript::dump(QTextStream &out) const void Member::dump(QTextStream &out) const { - base->dump(out); + if (kind != MemberOfEnum && attachedPropertiesIdOrEnumValue != 0 && !base->asTemp()) + out << "[[attached property from " << attachedPropertiesIdOrEnumValue << "]]"; + else + base->dump(out); out << '.' << *name; - if (type == MemberOfQObject) + if (property) out << " (meta-property " << property->coreIndex << " <" << QMetaType::typeName(property->propType) << ">)"; } @@ -825,24 +843,10 @@ Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) return e; } -Expr *BasicBlock::MEMBER(Expr *base, const QString *name) -{ - Member*e = function->New<Member>(); - e->init(base, name); - return e; -} - -Expr *BasicBlock::QML_CONTEXT_MEMBER(Expr *base, const QString *id, int memberIndex) -{ - Member*e = function->New<Member>(); - e->initQmlContextMember(base, id, memberIndex); - return e; -} - -Expr *BasicBlock::QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property) +Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue) { Member*e = function->New<Member>(); - e->initMetaProperty(base, id, property); + e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue); return e; } @@ -1032,14 +1036,8 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { - if (e->type == Member::MemberByName) - cloned = block->MEMBER(clone(e->base), e->name); - else if (e->type == Member::MemberOfQmlContext) - cloned = block->QML_CONTEXT_MEMBER(clone(e->base), e->name, e->memberIndex); - else if (e->type == Member::MemberOfQObject) - cloned = block->QML_QOBJECT_PROPERTY(clone(e->base), e->name, e->property); - else - Q_ASSERT(!"Unimplemented!"); + Expr *clonedBase = clone(e->base); + cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->attachedPropertiesIdOrEnumValue); } } // end of namespace IR diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 9a1bd87a1d..2eba3405fe 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -55,6 +55,7 @@ #include "private/qv4global_p.h" #include <private/qqmljsmemorypool_p.h> #include <private/qqmljsastfwd_p.h> +#include <private/qflagpointer_p.h> #include <QtCore/QVector> #include <QtCore/QString> @@ -73,6 +74,8 @@ QT_BEGIN_NAMESPACE class QTextStream; class QQmlType; class QQmlPropertyData; +class QQmlPropertyCache; +class QQmlEnginePrivate; namespace QV4 { struct ExecutionContext; @@ -122,6 +125,14 @@ struct CJump; struct Ret; struct Phi; +// Flag pointer: +// * The first flag indicates whether the meta object is final. +// If final, then none of its properties themselves need to +// be final when considering for lookups in QML. +// * The second flag indicates whether enums should be included +// in the lookup of properties or not. The default is false. +typedef QFlagPointer<QQmlPropertyCache> IRMetaObject; + enum AluOp { OpInvalid = 0, @@ -181,7 +192,8 @@ enum Type { NumberType = SInt32Type | UInt32Type | DoubleType, StringType = 1 << 7, - VarType = 1 << 8 + QObjectType = 1 << 8, + VarType = 1 << 9 }; inline bool strictlyEqualTypes(Type t1, Type t2) @@ -218,6 +230,24 @@ struct StmtVisitor { virtual void visitPhi(Phi *) = 0; }; + +struct MemberExpressionResolver +{ + typedef Type (*ResolveFunction)(QQmlEnginePrivate *engine, MemberExpressionResolver *resolver, Member *member); + + MemberExpressionResolver() + : resolveMember(0), data(0), extraData(0), flags(0), isQObjectResolver(false) {} + + bool isValid() const { return !!resolveMember; } + void clear() { *this = MemberExpressionResolver(); } + + ResolveFunction resolveMember; + void *data; // Could be pointer to meta object, importNameSpace, etc. - depends on resolveMember implementation + void *extraData; // Could be QQmlTypeNameCache + unsigned int flags : 31; + unsigned int isQObjectResolver; // neede for IR dump helpers +}; + struct Expr { Type type; @@ -325,7 +355,7 @@ struct Name: Expr { builtin_define_object_literal, builtin_setup_argument_object, builtin_convert_this_to_object, - builtin_qml_id_scope, + builtin_qml_id_array, builtin_qml_imported_scripts_object, builtin_qml_context_object, builtin_qml_scope_object @@ -333,7 +363,9 @@ struct Name: Expr { const QString *id; Builtin builtin; - bool global; + bool global : 1; + bool qmlSingleton : 1; + bool freeOfSideEffects : 1; quint32 line; quint32 column; @@ -360,9 +392,12 @@ struct Temp: Expr { }; unsigned index; - unsigned scope : 28; // how many scopes outside the current one? + unsigned scope : 27; // how many scopes outside the current one? unsigned kind : 3; unsigned isArgumentsOrEval : 1; + unsigned isReadOnly : 1; + // Used when temp is used as base in member expression + MemberExpressionResolver memberResolver; void init(unsigned kind, unsigned index, unsigned scope) { @@ -374,10 +409,11 @@ struct Temp: Expr { this->index = index; this->scope = scope; this->isArgumentsOrEval = false; + this->isReadOnly = false; } virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return true; } + virtual bool isLValue() { return !isReadOnly; } virtual Temp *asTemp() { return this; } virtual void dump(QTextStream &out) const; @@ -518,47 +554,53 @@ struct Subscript: Expr { }; struct Member: Expr { - enum MemberType { - MemberByName, - // QML extensions - MemberOfQmlContext, // lookup in context's id values - MemberOfQObject + // Used for property dependency tracking + enum MemberKind { + UnspecifiedMember, + MemberOfEnum, + MemberOfQmlScopeObject, + MemberOfQmlContextObject }; - MemberType type; Expr *base; const QString *name; - int memberIndex; // used if type == MemberOfQmlContext QQmlPropertyData *property; + int attachedPropertiesIdOrEnumValue; // depending on kind + uchar memberIsEnum : 1; + uchar freeOfSideEffects : 1; - void init(Expr *base, const QString *name) - { - this->type = MemberByName; - this->base = base; - this->name = name; - this->memberIndex = -1; - this->property = 0; + // This is set for example for for QObject properties. All sorts of extra behavior + // is defined when writing to them, for example resettable properties are reset + // when writing undefined to them, and an exception is thrown when they're missing + // a reset function. And then there's also Qt.binding(). + uchar inhibitTypeConversionOnWrite: 1; + + uchar kind: 3; // MemberKind + + void setEnumValue(int value) { + kind = MemberOfEnum; + attachedPropertiesIdOrEnumValue = value; } - void initQmlContextMember(Expr *base, const QString *name, int memberIndex) - { - this->type = MemberOfQmlContext; - this->base = base; - this->name = name; - this->memberIndex = memberIndex; - this->property = 0; + void setAttachedPropertiesId(int id) { + Q_ASSERT(kind != MemberOfEnum); + attachedPropertiesIdOrEnumValue = id; } - void initMetaProperty(Expr *base, const QString *name, QQmlPropertyData *property) + void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0) { - this->type = MemberOfQObject; this->base = base; this->name = name; this->property = property; + this->attachedPropertiesIdOrEnumValue = attachedPropertiesIdOrEnumValue; + this->memberIsEnum = false; + this->freeOfSideEffects = false; + this->inhibitTypeConversionOnWrite = property != 0; + this->kind = kind; } virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return type != MemberOfQmlContext; } + virtual bool isLValue() { return true; } virtual Member *asMember() { return this; } virtual void dump(QTextStream &out) const; @@ -717,6 +759,9 @@ struct Q_QML_EXPORT Module { void setFileName(const QString &name); }; +// Map from meta property index (existence implies dependency) to notify signal index +typedef QHash<int, int> PropertyDependencyMap; + struct Function { Module *module; MemoryPool *pool; @@ -747,10 +792,8 @@ struct Function { // Qml extension: QSet<int> idObjectDependencies; - QSet<QQmlPropertyData*> contextObjectDependencies; - QSet<QQmlPropertyData*> scopeObjectDependencies; - - bool hasQmlDependencies() const { return !idObjectDependencies.isEmpty() || !contextObjectDependencies.isEmpty() || !scopeObjectDependencies.isEmpty(); } + PropertyDependencyMap contextObjectPropertyDependencies; + PropertyDependencyMap scopeObjectPropertyDependencies; template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } @@ -860,9 +903,7 @@ struct BasicBlock { Expr *CALL(Expr *base, ExprList *args = 0); Expr *NEW(Expr *base, ExprList *args = 0); Expr *SUBSCRIPT(Expr *base, Expr *index); - Expr *MEMBER(Expr *base, const QString *name); - Expr *QML_CONTEXT_MEMBER(Expr *base, const QString *id, int memberIndex); - Expr *QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property); + Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0); Stmt *EXP(Expr *expr); @@ -927,6 +968,8 @@ public: newName->id = n->id; newName->builtin = n->builtin; newName->global = n->global; + newName->qmlSingleton = n->qmlSingleton; + newName->freeOfSideEffects = n->freeOfSideEffects; newName->line = n->line; newName->column = n->column; return newName; @@ -937,6 +980,7 @@ public: Temp *newTemp = f->New<Temp>(); newTemp->init(t->kind, t->index, t->scope); newTemp->type = t->type; + newTemp->memberResolver = t->memberResolver; return newTemp; } diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index a6e66d2722..93ecdb2602 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -338,9 +338,8 @@ protected: // IRDecoder addDef(temp); } - virtual void loadQmlIdObject(int id, V4IR::Temp *temp) + virtual void loadQmlIdArray(V4IR::Temp *temp) { - Q_UNUSED(id); addDef(temp); addCall(); } @@ -365,6 +364,14 @@ protected: // IRDecoder addCall(); } + virtual void loadQmlSingleton(const QString &/*name*/, Temp *temp) + { + Q_UNUSED(temp); + + addDef(temp); + addCall(); + } + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { Q_UNUSED(sourceConst); @@ -428,7 +435,7 @@ protected: // IRDecoder addCall(); } - virtual void getQObjectProperty(V4IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, V4IR::Temp *target) + virtual void getQObjectProperty(V4IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, int /*attachedPropertiesId*/, V4IR::Temp *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); @@ -511,8 +518,15 @@ protected: // IRDecoder || (oper >= OpGt && oper <= OpStrictNotEqual)) { needsCall = false; } - } if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) { + } else if (oper == OpBitAnd || oper == OpBitOr || oper == OpBitXor || oper == OpLShift || oper == OpRShift || oper == OpURShift) { needsCall = false; + } else if (oper == OpAdd + || oper == OpMul + || + oper == OpSub + ) { + if (leftSource->type == SInt32Type && rightSource->type == SInt32Type) + needsCall = false; } addDef(target); @@ -589,6 +603,7 @@ private: Q_ASSERT(!_defs.contains(*t)); bool canHaveReg = true; switch (t->type) { + case QObjectType: case VarType: case StringType: case UndefinedType: @@ -889,9 +904,9 @@ private: } } if (!moveFrom) { - Q_ASSERT(!_info->isPhiTarget(it.temp()) || it.isSplitFromInterval() || lifeTimeHole); Q_UNUSED(lifeTimeHole); #if !defined(QT_NO_DEBUG) + Q_ASSERT(!_info->isPhiTarget(it.temp()) || it.isSplitFromInterval() || lifeTimeHole); if (_info->def(it.temp()) != successorStart && !it.isSplitFromInterval()) { const int successorEnd = successor->statements.last()->id; const int idx = successor->in.indexOf(predecessor); diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 6b1169d30a..4fee3c04a0 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -52,6 +52,7 @@ #include <qv4runtime_p.h> #include <qv4context_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmlengine_p.h> #include <cmath> #include <iostream> #include <cassert> @@ -212,6 +213,22 @@ void showMeTheCode(Function *function) } } +inline Temp *unescapableTemp(Expr *e, bool variablesCanEscape) +{ + Temp *t = e->asTemp(); + if (!t) + return 0; + + switch (t->kind) { + case Temp::VirtualRegister: + return t; + case Temp::Local: + return variablesCanEscape ? 0 : t; + default: + return 0; + } +} + class DominatorTree { int N; QHash<BasicBlock *, int> dfnum; @@ -625,15 +642,98 @@ void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) { } } +// High-level (recursive) algorithm: +// Mapping: old temp number -> new temp number +// +// Start: +// Rename(start-node) +// +// Rename(node, mapping): +// for each statement S in block n +// if S not in a phi-function +// for each use of some variable x in S +// y = mapping[x] +// replace the use of x with y in S +// for each definition of some variable a in S [1] +// a_new = generate new/unique temp +// mapping[a] = a_new +// replace definition of a with definition of a_new in S +// for each successor Y of block n +// Suppose n is the j-th predecessor of Y +// for each phi function in Y +// suppose the j-th operand of the phi-function is a +// i = mapping[a] +// replace the j-th operand with a_i +// for each child X of n [2] +// Rename(X) +// for each newly generated temp from step [1] restore the old value [3] +// +// This algorithm can run out of CPU stack space when there are lots of basic-blocks, like in a +// switch statement with 8000 cases that all fall-through. The iterativer version below uses a +// work-item stack, where step [1] from the algorithm above also pushes an "undo mapping change", +// and step [2] pushes a "rename(X)" action. This eliminates step [3]. +// +// Iterative version: +// Mapping: old temp number -> new temp number +// +// The stack can hold two kinds of actions: +// "Rename basic block n" +// "Restore count for temp" +// +// Start: +// counter = 0 +// push "Rename start node" onto the stack +// while the stack is not empty: +// take the last item, and process it +// +// Rename(n) = +// for each statement S in block n +// if S not in a phi-function +// for each use of some variable x in S +// y = mapping[x] +// replace the use of x with y in S +// for each definition of some variable a in S +// old = mapping[a] +// push Undo(a, old) +// counter = counter + 1 +// new = counter; +// mapping[a] = new +// replace definition of a with definition of a_new in S +// for each successor Y of block n +// Suppose n is the j-th predecessor of Y +// for each phi function in Y +// suppose the j-th operand of the phi-function is a +// i = mapping[a] +// replace the j-th operand with a_i +// for each child X of n +// push Rename(X) +// +// Undo(t, c) = +// mapping[t] = c class VariableRenamer: public StmtVisitor, public ExprVisitor { Function *function; - QHash<Temp, QStack<unsigned> > stack; - QSet<BasicBlock *> seen; + const bool variablesCanEscape; + unsigned tempCount; - QHash<Temp, unsigned> defCounts; + typedef QHash<unsigned, int> Mapping; // maps from existing/old temp number to the new and unique temp number. + enum { Absent = -1 }; + Mapping localMapping; + Mapping vregMapping; + QBitArray processed; + + bool alreadyProcessed(BasicBlock *bb) const + { + Q_ASSERT(bb); + + return processed.at(bb->index); + } + + void markAsProcessed(BasicBlock *bb) + { + processed.setBit(bb->index); + } - const bool variablesCanEscape; bool isRenamable(Temp *t) const { switch (t->kind) { @@ -650,99 +750,125 @@ class VariableRenamer: public StmtVisitor, public ExprVisitor return false; } } - int nextFreeTemp() { - const int next = function->tempCount++; -// qDebug()<<"Next free temp:"<<next; - return next; - } - - /* - - Initialization: - for each variable a - count[a] = 0; - stack[a] = empty; - push 0 onto stack - - Rename(n) = - for each statement S in block n [1] - if S not in a phi-function - for each use of some variable x in S - i = top(stack[x]) - replace the use of x with x_i in S - for each definition of some variable a in S - count[a] = count[a] + 1 - i = count[a] - push i onto stack[a] - replace definition of a with definition of a_i in S - for each successor Y of block n [2] - Suppose n is the j-th predecessor of Y - for each phi function in Y - suppose the j-th operand of the phi-function is a - i = top(stack[a]) - replace the j-th operand with a_i - for each child X of n [3] - Rename(X) - for each statement S in block n [4] - for each definition of some variable a in S - pop stack[a] - - */ + + struct TodoAction { + enum { RestoreLocal, RestoreVReg, Rename } action; + union { + struct { + unsigned temp; + int previous; + } restoreData; + struct { + BasicBlock *basicBlock; + } renameData; + }; + + bool isValid() const { return action != Rename || renameData.basicBlock != 0; } + + TodoAction() + { + action = Rename; + renameData.basicBlock = 0; + } + + TodoAction(const Temp &t, int prev) + { + Q_ASSERT(t.kind == Temp::Local || t.kind == Temp::VirtualRegister); + + action = t.kind == Temp::Local ? RestoreLocal : RestoreVReg; + restoreData.temp = t.index; + restoreData.previous = prev; + } + + TodoAction(BasicBlock *bb) + { + Q_ASSERT(bb); + + action = Rename; + renameData.basicBlock = bb; + } + }; + + QVector<TodoAction> todo; public: VariableRenamer(Function *f) : function(f) , variablesCanEscape(f->variablesCanEscape()) + , tempCount(0) { - if (!variablesCanEscape) { - Temp t; - t.init(Temp::Local, 0, 0); - for (int i = 0, ei = f->locals.size(); i != ei; ++i) { - t.index = i; - stack[t].push(nextFreeTemp()); + int maxBB = 0; + foreach (BasicBlock *bb, f->basicBlocks) + maxBB = qMax(maxBB, bb->index); + processed = QBitArray(maxBB + 1, false); + } + + void run() { + todo.append(TodoAction(function->basicBlocks.first())); + + while (!todo.isEmpty()) { + TodoAction todoAction = todo.back(); + Q_ASSERT(todoAction.isValid()); + todo.pop_back(); + + switch (todoAction.action) { + case TodoAction::Rename: + rename(todoAction.renameData.basicBlock); + break; + case TodoAction::RestoreLocal: + restore(localMapping, todoAction.restoreData.temp, todoAction.restoreData.previous); + break; + case TodoAction::RestoreVReg: + restore(vregMapping, todoAction.restoreData.temp, todoAction.restoreData.previous); + break; + default: + Q_UNREACHABLE(); } } - Temp t; - t.init(Temp::VirtualRegister, 0, 0); - for (int i = 0, ei = f->tempCount; i != ei; ++i) { - t.index = i; - stack[t].push(i); - } + function->tempCount = tempCount; } - void run() { - foreach (BasicBlock *n, function->basicBlocks) - rename(n); - -#ifdef SHOW_SSA -// qout << "Temp to local mapping:" << endl; -// foreach (int key, tempMapping.keys()) -// qout << '\t' << key << " -> " << tempMapping[key] << endl; -#endif +private: + static inline void restore(Mapping &mapping, unsigned temp, int previous) + { + if (previous == Absent) + mapping.remove(temp); + else + mapping[temp] = previous; } - void rename(BasicBlock *n) { - if (seen.contains(n)) - return; - seen.insert(n); -// qDebug() << "I: L"<<n->index; + void rename(BasicBlock *bb) + { + while (bb && !alreadyProcessed(bb)) { + renameStatementsAndPhis(bb); + markAsProcessed(bb); - // [1]: - foreach (Stmt *s, n->statements) - s->accept(this); + BasicBlock *next = 0; + foreach (BasicBlock *out, bb->out) { + if (alreadyProcessed(out)) + continue; + if (!next) + next = out; + else + todo.append(TodoAction(out)); + } + bb = next; + } + } - QHash<Temp, unsigned> dc = defCounts; - defCounts.clear(); + void renameStatementsAndPhis(BasicBlock *bb) + { + foreach (Stmt *s, bb->statements) + s->accept(this); - // [2]: - foreach (BasicBlock *Y, n->out) { - const int j = Y->in.indexOf(n); + foreach (BasicBlock *Y, bb->out) { + const int j = Y->in.indexOf(bb); Q_ASSERT(j >= 0 && j < Y->in.size()); foreach (Stmt *s, Y->statements) { if (Phi *phi = s->asPhi()) { Temp *t = phi->d->incoming[j]->asTemp(); - unsigned newTmp = stack[*t].top(); + unsigned newTmp = currentNumber(*t); // qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index; t->index = newTmp; t->kind = Temp::VirtualRegister; @@ -751,24 +877,65 @@ public: } } } + } - // [3]: - foreach (BasicBlock *X, n->out) - rename(X); + unsigned currentNumber(const Temp &t) + { + int nr = Absent; + switch (t.kind) { + case Temp::Local: + nr = localMapping.value(t.index, Absent); + break; + case Temp::VirtualRegister: + nr = vregMapping.value(t.index, Absent); + break; + default: + Q_UNREACHABLE(); + nr = Absent; + break; + } + if (nr == Absent) { + // Special case: we didn't prune the Phi nodes yet, so for proper temps (virtual + // registers) the SSA algorithm might insert superfluous Phis that have uses without + // definition. E.g.: if a temporary got introduced in the "then" clause, it "could" + // reach the "end-if" block, so there will be a phi node for that temp. A later pass + // will clean this up by looking for uses-without-defines in phi nodes. So, what we do + // is to generate a new unique number, and leave it dangling. + nr = nextFreeTemp(t); + } + + return nr; + } + + unsigned nextFreeTemp(const Temp &t) + { + unsigned newIndex = tempCount++; + Q_ASSERT(newIndex <= INT_MAX); + int oldIndex = Absent; - // [4]: - for (QHash<Temp, unsigned>::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) { -// qDebug()<<i.key() <<" -> " << i.value(); - for (unsigned j = 0, ej = i.value(); j < ej; ++j) - stack[i.key()].pop(); + switch (t.kind) { + case Temp::Local: + oldIndex = localMapping.value(t.index, Absent); + localMapping.insert(t.index, newIndex); + break; + case Temp::VirtualRegister: + oldIndex = vregMapping.value(t.index, Absent); + vregMapping.insert(t.index, newIndex); + break; + default: + Q_UNREACHABLE(); } + + todo.append(TodoAction(t, oldIndex)); + + return newIndex; } protected: virtual void visitTemp(Temp *e) { // only called for uses, not defs if (isRenamable(e)) { // qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top(); - e->index = stack[*e].top(); + e->index = currentNumber(*e); e->kind = Temp::VirtualRegister; } } @@ -786,9 +953,7 @@ protected: void renameTemp(Temp *t) { if (isRenamable(t)) { - defCounts[*t] = defCounts.value(*t, 0) + 1; - const int newIdx = nextFreeTemp(); - stack[*t].push(newIdx); + const int newIdx = nextFreeTemp(*t); // qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx; t->kind = Temp::VirtualRegister; t->index = newIdx; @@ -874,12 +1039,15 @@ void convertToSSA(Function *function, const DominatorTree &df) struct UntypedTemp { Temp temp; + UntypedTemp() {} UntypedTemp(const Temp &t): temp(t) {} }; inline uint qHash(const UntypedTemp &t, uint seed = 0) Q_DECL_NOTHROW { return t.temp.index ^ (t.temp.kind | (t.temp.scope << 3)) ^ seed; } inline bool operator==(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW { return t1.temp.index == t2.temp.index && t1.temp.scope == t2.temp.scope && t1.temp.kind == t2.temp.kind; } +inline bool operator!=(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW +{ return !(t1 == t2); } class DefUsesCalculator: public StmtVisitor, public ExprVisitor { public: @@ -964,6 +1132,8 @@ public: defUse.blockOfStatement = defBlock; } + QList<UntypedTemp> defsUntyped() const { return _defUses.keys(); } + QList<Temp> defs() const { QList<Temp> res; res.reserve(_defUses.size()); @@ -982,10 +1152,10 @@ public: void addUse(const Temp &variable, Stmt * newUse) { _defUses[variable].uses.append(newUse); } - int useCount(const Temp &variable) const + int useCount(const UntypedTemp &variable) const { return _defUses[variable].uses.size(); } - Stmt *defStmt(const Temp &variable) const + Stmt *defStmt(const UntypedTemp &variable) const { return _defUses[variable].defStmt; } BasicBlock *defStmtBlock(const Temp &variable) const @@ -997,7 +1167,7 @@ public: QList<Temp> usedVars(Stmt *s) const { return _usesPerStatement[s]; } - QList<Stmt *> uses(const Temp &var) const + QList<Stmt *> uses(const UntypedTemp &var) const { return _defUses[var].uses; } QVector<Stmt*> removeDefUses(Stmt *s) @@ -1188,6 +1358,8 @@ protected: virtual void visitName(Name *e) { + if (e->freeOfSideEffects) + return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have // a side-effect. if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) @@ -1209,6 +1381,7 @@ protected: e->expr->accept(this); switch (e->expr->type) { + case QObjectType: case StringType: case VarType: markAsSideEffect(); @@ -1227,7 +1400,7 @@ protected: case OpNot: case OpIncrement: case OpDecrement: - if (e->expr->type == VarType || e->expr->type == StringType) + if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType) markAsSideEffect(); break; @@ -1243,8 +1416,8 @@ protected: _sideEffect = checkForSideEffects(e->left); _sideEffect |= checkForSideEffects(e->right); - if (e->left->type == VarType || e->left->type == StringType - || e->right->type == VarType || e->right->type == StringType) + if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType + || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType) markAsSideEffect(); } @@ -1256,6 +1429,8 @@ protected: virtual void visitMember(Member *e) { e->base->accept(this); + if (e->freeOfSideEffects) + return; markAsSideEffect(); } @@ -1274,23 +1449,115 @@ protected: } }; +struct DiscoveredType { + int type; + MemberExpressionResolver memberResolver; + + DiscoveredType() : type(UnknownType) {} + DiscoveredType(Type t) : type(t) { Q_ASSERT(type != QObjectType); } + explicit DiscoveredType(int t) : type(t) { Q_ASSERT(type != QObjectType); } + explicit DiscoveredType(MemberExpressionResolver memberResolver) : type(QObjectType), memberResolver(memberResolver) {} + + bool test(Type t) const { return type & t; } + bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); } + + bool operator!=(Type other) const { return type != other; } + bool operator==(Type other) const { return type == other; } + bool operator==(const DiscoveredType &other) const { return type == other.type; } + bool operator!=(const DiscoveredType &other) const { return type != other.type; } +}; + +class PropagateTempTypes: public StmtVisitor, ExprVisitor +{ + const DefUsesCalculator &defUses; + UntypedTemp theTemp; + DiscoveredType newType; + +public: + PropagateTempTypes(const DefUsesCalculator &defUses) + : defUses(defUses) + {} + + void run(const UntypedTemp &temp, const DiscoveredType &type) + { + newType = type; + theTemp = temp; + if (Stmt *defStmt = defUses.defStmt(temp)) + defStmt->accept(this); + foreach (Stmt *use, defUses.uses(temp)) + use->accept(this); + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *e) { + if (theTemp == UntypedTemp(*e)) { + e->type = static_cast<Type>(newType.type); + e->memberResolver = newType.memberResolver; + } + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(Subscript *e) { + e->base->accept(this); + e->index->accept(this); + } + + virtual void visitMember(Member *e) { + e->base->accept(this); + } + + virtual void visitExp(Exp *s) {s->expr->accept(this);} + virtual void visitMove(Move *s) { + s->source->accept(this); + s->target->accept(this); + } + + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitPhi(Phi *s) { + s->targetTemp->accept(this); + foreach (Expr *e, s->d->incoming) + e->accept(this); + } +}; + class TypeInference: public StmtVisitor, public ExprVisitor { + QQmlEnginePrivate *qmlEngine; bool _variablesCanEscape; const DefUsesCalculator &_defUses; - QHash<Temp, int> _tempTypes; + QHash<Temp, DiscoveredType> _tempTypes; QSet<Stmt *> _worklist; struct TypingResult { - int type; + DiscoveredType type; bool fullyTyped; - TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} - explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} + TypingResult(const DiscoveredType &type = DiscoveredType()) : type(type), fullyTyped(type.type != UnknownType) {} + explicit TypingResult(MemberExpressionResolver memberResolver): type(memberResolver), fullyTyped(true) {} }; TypingResult _ty; public: - TypeInference(const DefUsesCalculator &defUses) - : _defUses(defUses) + TypeInference(QQmlEnginePrivate *qmlEngine, const DefUsesCalculator &defUses) + : qmlEngine(qmlEngine) + , _defUses(defUses) , _ty(UnknownType) {} @@ -1329,74 +1596,12 @@ public: } } - PropagateTempTypes(_tempTypes).run(function); + PropagateTempTypes propagator(_defUses); + for (QHash<Temp, DiscoveredType>::const_iterator i = _tempTypes.begin(), ei = _tempTypes.end(); i != ei; ++i) + propagator.run(i.key(), i.value()); } private: - class PropagateTempTypes: public StmtVisitor, ExprVisitor - { - public: - PropagateTempTypes(const QHash<Temp, int> &tempTypes) - : _tempTypes(tempTypes) - {} - - void run(Function *function) - { - foreach (BasicBlock *bb, function->basicBlocks) - foreach (Stmt *s, bb->statements) - s->accept(this); - } - - protected: - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { e->type = (Type) _tempTypes[*e]; } - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(Member *e) { - e->base->accept(this); - } - - virtual void visitExp(Exp *s) {s->expr->accept(this);} - virtual void visitMove(Move *s) { - s->source->accept(this); - s->target->accept(this); - } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *s) { - s->targetTemp->accept(this); - foreach (Expr *e, s->d->incoming) - e->accept(this); - } - - private: - QHash<Temp, int> _tempTypes; - }; - -private: bool run(Stmt *s) { TypingResult ty; std::swap(_ty, ty); @@ -1416,26 +1621,29 @@ private: return ty; } - bool isAlwaysAnObject(Temp *t) { + bool isAlwaysVar(Temp *t) { switch (t->kind) { case Temp::Formal: case Temp::ScopedFormal: case Temp::ScopedLocal: + t->type = VarType; return true; case Temp::Local: + if (_variablesCanEscape) + t->type = VarType; return _variablesCanEscape; default: return false; } } - void setType(Expr *e, int ty) { + void setType(Expr *e, DiscoveredType ty) { if (Temp *t = e->asTemp()) { #if defined(SHOW_SSA) qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <<t->index<< " to "<<typeName(Type(ty)) << " (" << ty << ")" << endl; #endif - if (isAlwaysAnObject(t)) - ty = VarType; + if (isAlwaysVar(t)) + ty = DiscoveredType(VarType); if (_tempTypes[*t] != ty) { _tempTypes[*t] = ty; @@ -1450,7 +1658,7 @@ private: _worklist += QSet<Stmt *>::fromList(_defUses.uses(*t)); } } else { - e->type = (Type) ty; + e->type = (Type) ty.type; } } @@ -1470,10 +1678,12 @@ protected: virtual void visitRegExp(RegExp *) { _ty = TypingResult(VarType); } virtual void visitName(Name *) { _ty = TypingResult(VarType); } virtual void visitTemp(Temp *e) { - if (isAlwaysAnObject(e)) + if (isAlwaysVar(e)) _ty = TypingResult(VarType); + else if (e->memberResolver.isValid()) + _ty = TypingResult(e->memberResolver); else - _ty = TypingResult(_tempTypes.value(*e, UnknownType)); + _ty = TypingResult(_tempTypes.value(*e)); setType(e, _ty.type); } virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); } @@ -1505,9 +1715,9 @@ protected: switch (e->op) { case OpAdd: - if (leftTy.type & VarType || rightTy.type & VarType) + if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType)) _ty.type = VarType; - else if (leftTy.type & StringType || rightTy.type & StringType) + else if (leftTy.type.test(StringType) || rightTy.type.test(StringType)) _ty.type = StringType; else if (leftTy.type != UnknownType && rightTy.type != UnknownType) _ty.type = DoubleType; @@ -1574,14 +1784,13 @@ protected: } virtual void visitMember(Member *e) { - if (e->type == Member::MemberOfQObject - && !e->property->isEnum() // Enums need to go through run-time getters/setters to ensure correct string handling. - ) { - _ty = TypingResult(irTypeFromPropertyType(e->property->propType)); - return; - } _ty = run(e->base); - _ty.type = VarType; + + if (_ty.fullyTyped && _ty.type.memberResolver.isValid()) { + MemberExpressionResolver &resolver = _ty.type.memberResolver; + _ty.type.type = resolver.resolveMember(qmlEngine, &resolver, e); + } else + _ty.type = VarType; } virtual void visitExp(Exp *s) { _ty = run(s->expr); } @@ -1611,11 +1820,13 @@ protected: _ty.fullyTyped = false; break; } - _ty.type |= ty.type; + _ty.type.type |= ty.type.type; _ty.fullyTyped &= ty.fullyTyped; + if (_ty.type.test(QObjectType)) + _ty.type.memberResolver.clear(); // ### TODO: find common ancestor meta-object } - switch (_ty.type) { + switch (_ty.type.type) { case UnknownType: case UndefinedType: case NullType: @@ -1624,13 +1835,14 @@ protected: case UInt32Type: case DoubleType: case StringType: + case QObjectType: case VarType: // The type is not a combination of two or more types, so we're done. break; default: // There are multiple types involved, so: - if ((_ty.type & NumberType) && !(_ty.type & ~NumberType)) + if (_ty.type.isNumber()) // The type is any combination of double/int32/uint32, but nothing else. So we can // type it as double. _ty.type = DoubleType; @@ -1641,17 +1853,146 @@ protected: setType(s->targetTemp, _ty.type); } +}; + +class ReverseInference +{ + const DefUsesCalculator &_defUses; + bool _variablesCanExcape; + +public: + ReverseInference(const DefUsesCalculator &defUses) + : _defUses(defUses) + {} - static int irTypeFromPropertyType(int propType) + void run(Function *f) { - switch (propType) { - case QMetaType::Bool: return BoolType; - case QMetaType::Int: return SInt32Type; - case QMetaType::Double: return DoubleType; - case QMetaType::QString: return StringType; - default: break; + _variablesCanExcape = f->variablesCanEscape(); + + QTextStream os(stderr, QIODevice::WriteOnly); + + QVector<UntypedTemp> knownOk; + QList<UntypedTemp> candidates = _defUses.defsUntyped(); + while (!candidates.isEmpty()) { + UntypedTemp temp = candidates.last(); + candidates.removeLast(); + + if (knownOk.contains(temp)) + continue; + + if (!isUsedAsInt32(temp, knownOk)) + continue; + + Stmt *s = _defUses.defStmt(temp); + Move *m = s->asMove(); + if (!m) + continue; + Temp *target = m->target->asTemp(); + if (!target || temp != UntypedTemp(*target) || target->type == SInt32Type) + continue; + if (Temp *t = m->source->asTemp()) { + candidates.append(*t); + } else if (m->source->asConvert()) { + break; + } else if (Binop *b = m->source->asBinop()) { + switch (b->op) { + case OpAdd: + if (b->left->type & NumberType || b->right->type & NumberType) + break; + else + continue; + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpSub: + case OpMul: + case OpLShift: + case OpRShift: + case OpURShift: + break; + default: + continue; + } + if (Temp *lt = unescapableTemp(b->left, _variablesCanExcape)) + candidates.append(*lt); + if (Temp *rt = unescapableTemp(b->right, _variablesCanExcape)) + candidates.append(*rt); + } else if (Unop *u = m->source->asUnop()) { + if (u->op == OpCompl || u->op == OpUPlus) { + if (Temp *t = unescapableTemp(u->expr, _variablesCanExcape)) + candidates.append(*t); + } + } else { + continue; + } + + knownOk.append(temp); + } + + PropagateTempTypes propagator(_defUses); + foreach (const UntypedTemp &t, knownOk) { + propagator.run(t, SInt32Type); + if (Stmt *defStmt = _defUses.defStmt(t)) { + if (Move *m = defStmt->asMove()) { + if (Convert *c = m->source->asConvert()) + c->type = SInt32Type; + else if (Unop *u = m->source->asUnop()) + u->type = SInt32Type; + else if (Binop *b = m->source->asBinop()) + b->type = SInt32Type; + } + } + } + } + +private: + bool isUsedAsInt32(const UntypedTemp &t, const QVector<UntypedTemp> &knownOk) const + { + QList<Stmt *> uses = _defUses.uses(t); + if (uses.isEmpty()) + return false; + + foreach (Stmt *use, uses) { + if (Move *m = use->asMove()) { + Temp *targetTemp = m->target->asTemp(); + + if (m->source->asTemp()) { + if (!targetTemp || !knownOk.contains(*targetTemp)) + return false; + } else if (m->source->asConvert()) { + continue; + } else if (Binop *b = m->source->asBinop()) { + switch (b->op) { + case OpAdd: + case OpSub: + case OpMul: + if (!targetTemp || !knownOk.contains(*targetTemp)) + return false; + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpRShift: + case OpLShift: + case OpURShift: + continue; + default: + return false; + } + } else if (Unop *u = m->source->asUnop()) { + if (u->op == OpUPlus) { + if (!targetTemp || !knownOk.contains(*targetTemp)) + return false; + } else if (u->op != OpCompl) { + return false; + } + } else { + return false; + } + } else + return false; } - return VarType; + + return true; } }; @@ -1884,12 +2225,8 @@ protected: } } - // Don't convert when writing to QObject properties. All sorts of extra behavior - // is defined when writing to them, for example resettable properties are reset - // when writing undefined to them, and an exception is thrown when they're missing - // a reset function. const Member *targetMember = s->target->asMember(); - const bool inhibitConversion = targetMember && targetMember->type == Member::MemberOfQObject && targetMember->property; + const bool inhibitConversion = targetMember && targetMember->inhibitTypeConversionOnWrite; run(s->source, s->target->type, !inhibitConversion); } @@ -1964,7 +2301,7 @@ QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const Domin QSet<BasicBlock *> visited; QVector<BasicBlock *> &sequence; BasicBlock *currentGroup; - QList<BasicBlock *> postponed; + QSet<BasicBlock *> postponed; I(const DominatorTree &df, QVector<BasicBlock *> &sequence, QHash<BasicBlock *, BasicBlock *> &startEndLoops) @@ -1980,7 +2317,7 @@ QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const Domin return; if (bb->containingGroup() != currentGroup) { - postponed.append(bb); + postponed.insert(bb); return; } if (bb->isGroupStart()) @@ -2013,7 +2350,7 @@ QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const Domin if (bb->isGroupStart()) { currentGroup = bb->containingGroup(); startEndLoops.insert(bb, sequence.last()); - QList<BasicBlock *> p = postponed; + QSet<BasicBlock *> p = postponed; foreach (BasicBlock *pBB, p) DFS(pBB); } @@ -2022,7 +2359,7 @@ QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const Domin void layout(BasicBlock *bb) { sequence.append(bb); visited.insert(bb); - postponed.removeAll(bb); + postponed.remove(bb); } }; @@ -2259,22 +2596,6 @@ private: } }; -inline Temp *unescapableTemp(Expr *e, bool variablesCanEscape) -{ - Temp *t = e->asTemp(); - if (!t) - return 0; - - switch (t->kind) { - case Temp::VirtualRegister: - return t; - case Temp::Local: - return variablesCanEscape ? 0 : t; - default: - return 0; - } -} - namespace { /// This function removes the basic-block from the function's list, unlinks any uses and/or defs, /// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them @@ -2356,10 +2677,10 @@ bool tryOptimizingComparison(Expr *&expr) if (!b) return false; Const *leftConst = b->left->asConst(); - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType) + if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) return false; Const *rightConst = b->right->asConst(); - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType) + if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) return false; QV4::Primitive l = convertToValue(leftConst); @@ -2494,6 +2815,26 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) } continue; } + if (Member *member = m->source->asMember()) { + if (member->kind == Member::MemberOfEnum) { + Const *c = function->New<Const>(); + const int enumValue = member->attachedPropertiesIdOrEnumValue; + c->init(SInt32Type, enumValue); + W += replaceUses(targetTemp, c); + defUses.removeDef(*targetTemp); + *ref[s] = 0; + defUses.removeUse(s, *member->base->asTemp()); + continue; + } else if (member->attachedPropertiesIdOrEnumValue != 0 && member->property && member->base->asTemp()) { + // Attached properties have no dependency on their base. Isel doesn't + // need it and we can eliminate the temp used to initialize it. + defUses.removeUse(s, *member->base->asTemp()); + Const *c = function->New<Const>(); + c->init(SInt32Type, 0); + member->base = c; + continue; + } + } // copy propagation: if (Temp *sourceTemp = unescapableTemp(m->source, variablesCanEscape)) { @@ -2533,7 +2874,7 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) doneSomething = true; break; case OpUPlus: - constOperand->type = DoubleType; + constOperand->type = unop->type; doneSomething = true; break; case OpCompl: @@ -2566,14 +2907,41 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) } if (Binop *binop = m->source->asBinop()) { + Const *leftConst = binop->left->asConst(); + Const *rightConst = binop->right->asConst(); + + { // Typical casts to int32: + Expr *casted = 0; + switch (binop->op) { + case OpBitAnd: + if (leftConst && !rightConst && leftConst->value == 0xffffffff) + casted = rightConst; + else if (!leftConst && rightConst && rightConst->value == 0xffffffff) + casted = leftConst; + break; + case OpBitOr: + if (leftConst && !rightConst && leftConst->value == 0) + casted = rightConst; + else if (!leftConst && rightConst && rightConst->value == 0) + casted = leftConst; + break; + default: + break; + } + if (casted) { + Q_ASSERT(casted->type == SInt32Type); + m->source = casted; + W += m; + continue; + } + } + // TODO: More constant binary expression evaluation // TODO: If the result of the move is only used in one single cjump, then // inline the binop into the cjump. - Const *leftConst = binop->left->asConst(); - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType) + if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) continue; - Const *rightConst = binop->right->asConst(); - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType) + if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) continue; QV4::Primitive lc = convertToValue(leftConst); @@ -2957,7 +3325,7 @@ bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval &r1, const LifeTim return r1.temp() < r2.temp(); } -void Optimizer::run() +void Optimizer::run(QQmlEnginePrivate *qmlEngine) { #if defined(SHOW_SSA) qout << "##### NOW IN FUNCTION " << (function->name ? qPrintable(*function->name) : "anonymous!") @@ -2976,10 +3344,9 @@ void Optimizer::run() // showMeTheCode(function); static bool doSSA = qgetenv("QV4_NO_SSA").isEmpty(); - static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty(); if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA) { -// qout << "SSA for " << *function->name << endl; +// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl; // qout << "Starting edge splitting..." << endl; splitCriticalEdges(function); // showMeTheCode(function); @@ -2999,13 +3366,18 @@ void Optimizer::run() // showMeTheCode(function); // qout << "Running type inference..." << endl; - TypeInference(defUses).run(function); + TypeInference(qmlEngine, defUses).run(function); +// showMeTheCode(function); + +// qout << "Doing reverse inference..." << endl; + ReverseInference(defUses).run(function); // showMeTheCode(function); // qout << "Doing type propagation..." << endl; TypePropagation(defUses).run(function); // showMeTheCode(function); + static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty(); if (doOpt) { // qout << "Running SSA optimization..." << endl; optimizeSSA(function, defUses); diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index aa713d3fec..dcbc83ae65 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE class QTextStream; +class QQmlEnginePrivate; namespace QQmlJS { namespace V4IR { @@ -129,7 +130,7 @@ public: , inSSA(false) {} - void run(); + void run(QQmlEnginePrivate *qmlEngine); void convertOutOfSSA(); bool isInSSA() const diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp index 0ba939cdca..b83de937c6 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/qml/debugger/qqmlprofilerservice.cpp @@ -425,4 +425,204 @@ void QQmlProfilerService::messageReceived(const QByteArray &message) m_initializeCondition.wakeAll(); } +/*! + * \brief QQmlVmeProfiler::Data::clear Reset to defaults + * Reset the profiling data to defaults. + */ +void QQmlVmeProfiler::Data::clear() +{ + url = QUrl(); + line = 0; + column = 0; + typeName = QString(); +} + +/*! + * \brief QQmlVmeProfiler::start Start profiler and set data + * \param url URL of file being executed + * \param line Curent line in file + * \param column Current column in file + * \param typeName Type of object be created + * Stops the profiler previously running in the foreground if there is one, then starts a + * new one and sets it up with the data given. + * Preconditions: Profiling must be enabled. + */ +void QQmlVmeProfiler::start(const QUrl &url, int line, int column, const QString &typeName) +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + if (enabled) { + switchRange(); + updateLocation(url, line, column); + updateTypeName(typeName); + } +} + +/*! + * \brief QQmlVmeProfiler::start Start profiler without data + * Clears the current range data, then stops the profiler previously running in the + * foreground if any, then starts a new one. + * Preconditions: Profiling must be enabled. + */ +void QQmlVmeProfiler::start() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + if (enabled) { + currentRange.clear(); + switchRange(); + } +} + +/*! + * \brief QQmlVmeProfiler::switchRange Switch foreground profilers + * Stops the current profiler if any, and starts a new one. + */ +void QQmlVmeProfiler::switchRange() +{ + if (running) + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + else + running = true; + QQmlProfilerService::instance->startRange(QQmlProfilerService::Creating); +} + +/*! + * \brief QQmlVmeProfiler::updateLocation Update current location information + * \param url URL of file being executed + * \param line line Curent line in file + * \param column column Current column in file + * Updates the current profiler's location information. + * Preconditions: Profiling must be enabled and a profiler must be running in the foreground. + */ +void QQmlVmeProfiler::updateLocation(const QUrl &url, int line, int column) +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(running, Q_FUNC_INFO, "trying to update location on stopped profiler"); + if (enabled && running) { + currentRange.url = url; + currentRange.line = line; + currentRange.column = column; + QQmlProfilerService::instance->rangeLocation( + QQmlProfilerService::Creating, url, line, column); + } +} + +/*! + * \brief QQmlVmeProfiler::updateTypeName Update current type information + * \param typeName Type of object being created + * Updates the current profiler's type information. + * Preconditions: Profiling must be enabled and a profiler must be running in the foreground. + */ +void QQmlVmeProfiler::updateTypeName(const QString &typeName) +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(running, Q_FUNC_INFO, "trying to update typeName on stopped profiler"); + if (enabled && running) { + currentRange.typeName = typeName; + QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, typeName); + } +} + +/*! + * \brief QQmlVmeProfiler::pop Pops a paused profiler from the stack and restarts it + * Stops the currently running profiler, if any, then retrieves an old one from the stack + * of paused profilers and starts that. + * Preconditions: Profiling must be enabled and there must be at least one profiler on the + * stack. + */ +void QQmlVmeProfiler::pop() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(ranges.count() > 0, Q_FUNC_INFO, "trying to pop an invalid profiler"); + if (enabled && ranges.count() > 0) { + start(); + currentRange = ranges.pop(); + QQmlProfilerService::instance->rangeLocation( + QQmlProfilerService::Creating, currentRange.url, currentRange.line, currentRange.column); + QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, currentRange.typeName); + } +} + +/*! + * \brief QQmlVmeProfiler::push Pushes the currently running profiler on the stack. + * Pushes the currently running profiler on the stack of paused profilers. Note: The profiler + * isn't paused here. That's a separate step. If it's never paused, but pop()'ed later that + * won't do any harm, though. + * Preconditions: Profiling must be enabled and a profiler must be running in the foreground. + */ +void QQmlVmeProfiler::push() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(running, Q_FUNC_INFO, "trying to push stopped profiler"); + if (enabled && running) + ranges.push(currentRange); +} + +/*! + * \brief QQmlVmeProfiler::clear Stop all running profilers and clear all data. + * Stops the currently running (foreground and background) profilers and removes all saved + * data about paused profilers. + * Precondtions: Profiling must be enabled. + */ +void QQmlVmeProfiler::clear() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + if (enabled) { + stop(); + ranges.clear(); + for (int i = 0; i < backgroundRanges.count(); ++i) { + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + } + backgroundRanges.clear(); + } +} + +/*! + * \brief QQmlVmeProfiler::stop Stop profiler running in the foreground, if any. + * Precondition: Profiling must be enabled. + */ +void QQmlVmeProfiler::stop() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + if (enabled && running) { + QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); + currentRange.clear(); + running = false; + } +} + +/*! + * \brief QQmlVmeProfiler::background Push the current profiler to the background. + * Push the profiler currently running in the foreground to the background so that it + * won't be stopped by stop() or start(). There can be multiple profilers in the background. + * You can retrieve them in reverse order by calling foreground(). + * Precondition: Profiling must be enabled and a profiler must be running in the foreground. + */ +void QQmlVmeProfiler::background() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(running, Q_FUNC_INFO, "trying to push stopped profiler to the background."); + if (enabled && running) { + backgroundRanges.push(currentRange); + running = false; + } +} + +/*! + * \brief QQmlVmeProfiler::foreground Retrieve a profiler from the background + * Stop the profiler currently running in the foreground, if any and put the next profiler + * from the background in its place. + * Preconditions: Profiling must be enabled and there must be at least one profiler in the + * background. + */ +void QQmlVmeProfiler::foreground() +{ + Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); + Q_ASSERT_X(backgroundRanges.count() > 0, Q_FUNC_INFO, "trying to foreground stopped profiler."); + if (enabled && backgroundRanges.count() > 0) { + stop(); + currentRange = backgroundRanges.pop(); + running = true; + } +} + QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index fb08a30c6a..5959a526bb 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -225,7 +225,7 @@ private: friend struct QQmlBindingProfiler; friend struct QQmlHandlingSignalProfiler; - friend struct QQmlObjectCreatingProfiler; + friend struct QQmlVmeProfiler; friend struct QQmlCompilingProfiler; friend struct QQmlPixmapProfiler; }; @@ -277,40 +277,6 @@ struct QQmlHandlingSignalProfiler { bool enabled; }; -struct QQmlObjectCreatingProfiler { - QQmlObjectCreatingProfiler() - { - enabled = QQmlProfilerService::instance - ? QQmlProfilerService::instance->profilingEnabled() : false; - if (enabled) { - QQmlProfilerService *service = QQmlProfilerService::instance; - service->startRange(QQmlProfilerService::Creating); - } - } - - void setTypeName(const QString &typeName) - { - Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); - QQmlProfilerService::instance->rangeData(QQmlProfilerService::Creating, typeName); - } - - void setLocation(const QUrl &url, int line, int column) - { - Q_ASSERT_X(enabled, Q_FUNC_INFO, "method called although profiler is not enabled."); - if (enabled) - QQmlProfilerService::instance->rangeLocation( - QQmlProfilerService::Creating, url, line, column); - } - - ~QQmlObjectCreatingProfiler() - { - if (enabled) - QQmlProfilerService::instance->endRange(QQmlProfilerService::Creating); - } - - bool enabled; -}; - struct QQmlCompilingProfiler { QQmlCompilingProfiler(const QString &name) { @@ -333,6 +299,54 @@ struct QQmlCompilingProfiler { bool enabled; }; +struct QQmlVmeProfiler { +public: + const bool enabled; + + struct Data { + Data() : line(0), column(0) {} + QUrl url; + int line; + int column; + QString typeName; + void clear(); + }; + + QQmlVmeProfiler() : + enabled(QQmlProfilerService::instance ? QQmlProfilerService::instance->profilingEnabled() : false), + running(false) + {} + + ~QQmlVmeProfiler() + { + if (enabled) + clear(); + } + + void clear(); + + void start(const QUrl &url, int line, int column, const QString &typeName); + void start(); + void stop(); + + void updateLocation(const QUrl &url, int line, int column); + void updateTypeName(const QString &typeName); + + void pop(); + void push(); + + void background(); + void foreground(); + +private: + void switchRange(); + + Data currentRange; + QStack<Data> ranges; + QStack<Data> backgroundRanges; + bool running; +}; + struct QQmlPixmapProfiler { QQmlPixmapProfiler() { QQmlProfilerService *instance = QQmlProfilerService::instance; @@ -367,9 +381,9 @@ struct QQmlPixmapProfiler { QQmlProfilerService::instance->pixmapEventImpl(QQmlProfilerService::PixmapReferenceCountChanged, pixmapUrl, referenceCount); } } - void setSize(const QUrl &pixmapUrl, int width, int height) { - if (enabled) { - QQmlProfilerService::instance->pixmapEventImpl(QQmlProfilerService::PixmapSizeKnown, pixmapUrl, width, height); + void setSize(const QUrl &pixmapUrl, const QSize &size) { + if (enabled && size.width() > 0) { + QQmlProfilerService::instance->pixmapEventImpl(QQmlProfilerService::PixmapSizeKnown, pixmapUrl, size.width(), size.height()); } } diff --git a/src/qml/debugger/qv4debugservice.cpp b/src/qml/debugger/qv4debugservice.cpp index 400bb18edd..372a51e997 100644 --- a/src/qml/debugger/qv4debugservice.cpp +++ b/src/qml/debugger/qv4debugservice.cpp @@ -432,7 +432,7 @@ public: QJsonArray scopes; // Only type and index are used by Qt Creator, so we keep it easy: - QVector<QV4::ExecutionContext::Type> scopeTypes = debugger->getScopeTypes(frameNr); + QVector<QV4::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) { int type = encodeScopeType(scopeTypes[i]); if (type == -1) @@ -448,7 +448,7 @@ public: return frame; } - int encodeScopeType(QV4::ExecutionContext::Type scopeType) + int encodeScopeType(QV4::ExecutionContext::ContextType scopeType) { switch (scopeType) { case QV4::ExecutionContext::Type_GlobalContext: @@ -480,7 +480,7 @@ public: QJsonObject anonymous; anonymous[QLatin1String("properties")] = properties; - QVector<QV4::ExecutionContext::Type> scopeTypes = debugger->getScopeTypes(frameNr); + QVector<QV4::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]); scope[QLatin1String("index")] = scopeNr; scope[QLatin1String("frameIndex")] = frameNr; @@ -619,7 +619,7 @@ public: addRunning(); QJsonObject body; body.insert(QStringLiteral("V8Version"), - QStringLiteral("this is not V8, this is V4 in Qt %1").arg(QLatin1String(QT_VERSION_STR))); + QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); addBody(body); } }; diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 5d8a0202fa..a19c358231 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -49,6 +49,7 @@ #include "private/qv4mm_p.h" #include "private/qv4globalobject_p.h" #include "private/qv4script_p.h" +#include "private/qv4runtime_p.h" #include <QtCore/qdatetime.h> #include <QtCore/qmetaobject.h> @@ -260,7 +261,7 @@ void QJSEngine::collectGarbage() QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { QV4::Scope scope(d->m_v4Engine); - QV4::ExecutionContext *ctx = d->m_v4Engine->current; + QV4::ExecutionContext *ctx = d->m_v4Engine->currentContext(); QV4::ScopedValue result(scope); QV4::Script script(ctx, program, fileName, lineNumber); @@ -376,6 +377,54 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) QV4::Scope scope(engine->m_v4Engine); QV4::ScopedValue v(scope, vp->getValue(engine->m_v4Engine)); return engine->metaTypeFromJS(v, type, ptr); + } else if (vp->value.isEmpty()) { + // have a string based value without engine. Do conversion manually + if (type == QMetaType::Bool) { + *reinterpret_cast<bool*>(ptr) = vp->string.length() != 0; + return true; + } + if (type == QMetaType::QString) { + *reinterpret_cast<QString*>(ptr) = vp->string; + return true; + } + double d = QV4::__qmljs_string_to_number(vp->string); + switch (type) { + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = QV4::Primitive::toInt32(d); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = QV4::Primitive::toUInt32(d); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = QV4::Primitive::toInteger(d); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = QV4::Primitive::toInteger(d); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = d; + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = d; + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = QV4::Primitive::toInt32(d); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = QV4::Primitive::toUInt32(d); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = QV4::Primitive::toInt32(d); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = QV4::Primitive::toUInt32(d); + return true; + case QMetaType::QChar: + *reinterpret_cast<QChar*>(ptr) = QV4::Primitive::toUInt32(d); + return true; + default: + return false; + } } else { switch (type) { case QMetaType::Bool: diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 4035fb9fa6..d7a1cef3ba 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -58,20 +58,22 @@ QV4::ReturnedValue QJSValuePrivate::getValue(QV4::ExecutionEngine *e) { - if (!this->engine) + if (!this->engine) { this->engine = e; - else if (this->engine != e) { + } else if (this->engine != e) { qWarning("JSValue can't be reassigned to another engine."); return QV4::Encode::undefined(); } - if (value.asString() == &string) { - value = QV4::Encode(engine->newString(string.toQString())); + + if (value.isEmpty()) { + value = QV4::Encode(engine->newString(string)); PersistentValuePrivate **listRoot = &engine->memoryManager->m_persistentValues; prev = listRoot; next = *listRoot; *prev = this; if (next) next->prev = &this->next; + string = QString(); } return value.asReturnedValue(); } @@ -210,7 +212,7 @@ QJSValue::QJSValue(const QLatin1String &value) */ #ifndef QT_NO_CAST_FROM_ASCII QJSValue::QJSValue(const char *value) - : d(new QJSValuePrivate(QString::fromLatin1(value))) + : d(new QJSValuePrivate(QString::fromUtf8(value))) { } #endif @@ -275,7 +277,7 @@ bool QJSValue::isNull() const */ bool QJSValue::isString() const { - return d->value.isString(); + return d->value.isEmpty() || d->value.isString(); } /*! @@ -359,6 +361,8 @@ bool QJSValue::isVariant() const */ QString QJSValue::toString() const { + if (d->value.isEmpty()) + return d->string; return d->value.toQStringNoThrow(); } @@ -376,7 +380,10 @@ QString QJSValue::toString() const */ double QJSValue::toNumber() const { - QV4::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + if (d->value.isEmpty()) + return __qmljs_string_to_number(d->string); + + QV4::ExecutionContext *ctx = d->engine ? d->engine->currentContext() : 0; double dbl = d->value.toNumber(); if (ctx && ctx->engine->hasException) { ctx->catchException(); @@ -399,7 +406,10 @@ double QJSValue::toNumber() const */ bool QJSValue::toBool() const { - QV4::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + if (d->value.isEmpty()) + return d->string.length() > 0; + + QV4::ExecutionContext *ctx = d->engine ? d->engine->currentContext() : 0; bool b = d->value.toBoolean(); if (ctx && ctx->engine->hasException) { ctx->catchException(); @@ -422,7 +432,10 @@ bool QJSValue::toBool() const */ qint32 QJSValue::toInt() const { - QV4::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + if (d->value.isEmpty()) + return QV4::Primitive::toInt32(__qmljs_string_to_number(d->string)); + + QV4::ExecutionContext *ctx = d->engine ? d->engine->currentContext() : 0; qint32 i = d->value.toInt32(); if (ctx && ctx->engine->hasException) { ctx->catchException(); @@ -445,7 +458,10 @@ qint32 QJSValue::toInt() const */ quint32 QJSValue::toUInt() const { - QV4::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + if (d->value.isEmpty()) + return QV4::Primitive::toUInt32(__qmljs_string_to_number(d->string)); + + QV4::ExecutionContext *ctx = d->engine ? d->engine->currentContext() : 0; quint32 u = d->value.toUInt32(); if (ctx && ctx->engine->hasException) { ctx->catchException(); @@ -478,6 +494,9 @@ quint32 QJSValue::toUInt() const */ QVariant QJSValue::toVariant() const { + if (d->value.isEmpty()) + return QVariant(d->string); + return QV4::VariantObject::toVariant(d->value); } @@ -517,7 +536,7 @@ QJSValue QJSValue::call(const QJSValueList &args) } ScopedValue result(scope); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); result = f->call(callData); if (scope.hasException()) result = ctx->catchException(); @@ -571,7 +590,7 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList } ScopedValue result(scope); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); result = f->call(callData); if (scope.hasException()) result = ctx->catchException(); @@ -617,7 +636,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) } ScopedValue result(scope); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); result = f->construct(callData); if (scope.hasException()) result = ctx->catchException(); @@ -720,6 +739,22 @@ QJSValue& QJSValue::operator=(const QJSValue& other) return *this; } +static bool js_equal(const QString &string, QV4::ValueRef value) +{ + if (value->isString()) + return string == value->stringValue()->toQString(); + if (value->isNumber()) + return __qmljs_string_to_number(string) == value->asDouble(); + if (value->isBoolean()) + return __qmljs_string_to_number(string) == value->booleanValue(); + if (value->isObject()) { + Scope scope(value->objectValue()->engine()); + ScopedValue p(scope, __qmljs_to_primitive(value, PREFERREDTYPE_HINT)); + return js_equal(string, p); + } + return false; +} + /*! Returns true if this QJSValue is equal to \a other, otherwise returns false. The comparison follows the behavior described in @@ -746,6 +781,14 @@ QJSValue& QJSValue::operator=(const QJSValue& other) */ bool QJSValue::equals(const QJSValue& other) const { + if (d->value.isEmpty()) { + if (other.d->value.isEmpty()) + return d->string == other.d->string; + return js_equal(d->string, QV4::ValueRef(other.d->value)); + } + if (other.d->value.isEmpty()) + return other.equals(*this); + return __qmljs_cmp_eq(QV4::ValueRef(d), QV4::ValueRef(other.d)); } @@ -773,6 +816,16 @@ bool QJSValue::equals(const QJSValue& other) const */ bool QJSValue::strictlyEquals(const QJSValue& other) const { + if (d->value.isEmpty()) { + if (other.d->value.isEmpty()) + return d->string == other.d->string; + if (other.d->value.isString()) + return d->string == other.d->value.stringValue()->toQString(); + return false; + } + if (other.d->value.isEmpty()) + return other.strictlyEquals(*this); + return __qmljs_strict_equal(QV4::ValueRef(d), QV4::ValueRef(other.d)); } @@ -806,7 +859,7 @@ QJSValue QJSValue::property(const QString& name) const return property(idx); s->makeIdentifier(); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); QV4::ScopedValue result(scope); result = o->get(s); if (scope.hasException()) @@ -838,7 +891,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const if (!o) return QJSValue(); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); QV4::ScopedValue result(scope); result = arrayIndex == UINT_MAX ? o->get(engine->id_uintMax) : o->getIndexed(arrayIndex); if (scope.hasException()) @@ -880,7 +933,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) return; } - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); s->makeIdentifier(); QV4::ScopedValue v(scope, value.d->getValue(engine)); o->put(s, v); @@ -911,7 +964,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) if (!o) return; - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); QV4::ScopedValue v(scope, value.d->getValue(engine)); if (arrayIndex != UINT_MAX) o->putIndexed(arrayIndex, v); @@ -944,7 +997,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) bool QJSValue::deleteProperty(const QString &name) { ExecutionEngine *engine = d->engine; - ExecutionContext *ctx = engine->current; + ExecutionContext *ctx = engine->currentContext(); Scope scope(engine); ScopedObject o(scope, d->value.asObject()); if (!o) diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index bf839a6f1f..d8da664cc6 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -80,17 +80,16 @@ public: Q_ASSERT(!value.isEmpty()); } QJSValuePrivate(const QString &s) - : PersistentValuePrivate(QV4::Encode::undefined()) - , string(0, s) + : PersistentValuePrivate(QV4::Primitive::emptyValue().asReturnedValue()) + , string(s) { - value.val = QV4::Encode(string.asReturned<QV4::String>()); } QV4::ReturnedValue getValue(QV4::ExecutionEngine *e); static QJSValuePrivate *get(const QJSValue &v) { return v.d; } - QV4::String string; + QString string; }; QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index 245b75b384..ed011ef691 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -59,7 +59,7 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) QV4::Scope scope(e); QV4::ScopedObject o(scope, jsp->value); - iterator = e->newForEachIteratorObject(e->current, o)->asReturnedValue(); + iterator = e->newForEachIteratorObject(e->currentContext(), o)->asReturnedValue(); currentName = (QV4::String *)0; nextName = (QV4::String *)0; @@ -198,7 +198,7 @@ QJSValue QJSValueIterator::value() const QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); QV4::ScopedObject o(scope, it->it.object); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); QV4::ScopedValue v(scope); if (!!d_ptr->currentName) { QV4::ScopedString n(scope, d_ptr->currentName); @@ -237,7 +237,7 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) QJSValuePrivate *jsp = QJSValuePrivate::get(object); QV4::Scope scope(v4); QV4::ScopedObject o(scope, jsp->value); - d_ptr->iterator = v4->newForEachIteratorObject(v4->current, o)->asReturnedValue(); + d_ptr->iterator = v4->newForEachIteratorObject(v4->currentContext(), o)->asReturnedValue(); QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); it->it.flags = QV4::ObjectIterator::NoFlags; it->it.next(d_ptr->nextName, &d_ptr->nextIndex, &d_ptr->nextAttributes); diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index ed010b1230..629c255b48 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -47,21 +47,21 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(ArgumentsObject); ArgumentsObject::ArgumentsObject(CallContext *context) - : Object(context->engine), context(context) + : Object(context->strictMode ? context->engine->strictArgumentsObjectClass : context->engine->argumentsObjectClass) + , context(context) + , fullyCreated(false) { - vtbl = &static_vtbl; type = Type_ArgumentsObject; + flags &= ~SimpleArray; ExecutionEngine *v4 = context->engine; Scope scope(v4); ScopedObject protectThis(scope, this); if (context->strictMode) { - internalClass = v4->strictArgumentsObjectClass; - Property pd = Property::fromAccessor(v4->thrower, v4->thrower); - assert(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); - assert(CallerPropertyIndex == internalClass->find(context->engine->id_caller)); + Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); + Q_ASSERT(CallerPropertyIndex == internalClass->find(context->engine->id_caller)); memberData[CalleePropertyIndex] = pd; memberData[CallerPropertyIndex] = pd; @@ -69,31 +69,17 @@ ArgumentsObject::ArgumentsObject(CallContext *context) for (int i = 0; i < context->callData->argc; ++i) arrayData[i].value = context->callData->args[i]; arrayDataLen = context->callData->argc; + fullyCreated = true; } else { - internalClass = engine()->argumentsObjectClass; Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); memberData[CalleePropertyIndex].value = context->function->asReturnedValue(); isNonStrictArgumentsObject = true; - - uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); - uint argCount = qMin(context->realArgumentCount, context->callData->argc); - arrayReserve(argCount); - ensureArrayAttributes(); - context->engine->requireArgumentsAccessors(numAccessors); - for (uint i = 0; i < (uint)numAccessors; ++i) { - mappedArguments.append(context->callData->args[i]); - arrayData[i] = context->engine->argumentsAccessors.at(i); - arrayAttributes[i] = Attr_Accessor; - } - for (uint i = numAccessors; i < argCount; ++i) { - arrayData[i] = Property::fromValue(context->callData->args[i]); - arrayAttributes[i] = Attr_Data; - } - arrayDataLen = argCount; } Q_ASSERT(LengthPropertyIndex == internalClass->find(context->engine->id_length)); Property *lp = memberData + ArrayObject::LengthPropertyIndex; lp->value = Primitive::fromInt32(context->realArgumentCount); + + Q_ASSERT(internalClass->vtable == &static_vtbl); } void ArgumentsObject::destroy(Managed *that) @@ -101,8 +87,34 @@ void ArgumentsObject::destroy(Managed *that) static_cast<ArgumentsObject *>(that)->~ArgumentsObject(); } +void ArgumentsObject::fullyCreate() +{ + if (fullyCreated) + return; + + uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); + uint argCount = qMin(context->realArgumentCount, context->callData->argc); + arrayReserve(argCount); + ensureArrayAttributes(); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->callData->args[i]); + arrayData[i] = context->engine->argumentsAccessors.at(i); + arrayAttributes[i] = Attr_Accessor; + } + for (uint i = numAccessors; i < argCount; ++i) { + arrayData[i] = Property::fromValue(context->callData->args[i]); + arrayAttributes[i] = Attr_Data; + } + arrayDataLen = argCount; + + fullyCreated = true; +} + bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) { + fullyCreate(); + Scope scope(ctx); uint pidx = propertyIndexFromArrayIndex(index); Property *pd = arrayData + pidx; @@ -143,6 +155,57 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const return result; } +ReturnedValue ArgumentsObject::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + if (args->fullyCreated) + return Object::getIndexed(m, index, hasProperty); + + if (index < static_cast<uint>(args->context->callData->argc)) { + if (hasProperty) + *hasProperty = true; + return args->context->callData->args[index].asReturnedValue(); + } + return Encode::undefined(); +} + +void ArgumentsObject::putIndexed(Managed *m, uint index, const ValueRef value) +{ + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + if (!args->fullyCreated && index >= static_cast<uint>(args->context->callData->argc)) + args->fullyCreate(); + + if (args->fullyCreated) { + Object::putIndexed(m, index, value); + return; + } + + args->context->callData->args[index] = value; +} + +bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) +{ + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + if (!args->fullyCreated) + args->fullyCreate(); + return Object::deleteIndexedProperty(m, index); +} + +PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +{ + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); + if (args->fullyCreated) + return Object::queryIndexed(m, index); + + uint numAccessors = qMin((int)args->context->function->formalParameterCount, args->context->realArgumentCount); + uint argCount = qMin(args->context->realArgumentCount, args->context->callData->argc); + if (index >= argCount) + return PropertyAttributes(); + if (index >= numAccessors) + return Attr_Data; + return Attr_Accessor; +} + DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) @@ -152,7 +215,7 @@ ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) Scoped<ArgumentsGetterFunction> g(scope, static_cast<ArgumentsGetterFunction *>(getter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); Q_ASSERT(g->index < static_cast<unsigned>(o->context->callData->argc)); return o->context->argument(g->index); @@ -167,7 +230,7 @@ ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData) Scoped<ArgumentsSetterFunction> s(scope, static_cast<ArgumentsSetterFunction *>(setter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); Q_ASSERT(s->index < static_cast<unsigned>(o->context->callData->argc)); o->context->callData->args[s->index] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined(); @@ -177,7 +240,7 @@ ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData) void ArgumentsObject::markObjects(Managed *that, ExecutionEngine *e) { ArgumentsObject *o = static_cast<ArgumentsObject *>(that); - o->context->mark(); + o->context->mark(e); for (int i = 0; i < o->mappedArguments.size(); ++i) o->mappedArguments.at(i).mark(e); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 7a5b0817a3..d306fae92b 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -54,7 +54,9 @@ struct ArgumentsGetterFunction: FunctionObject uint index; ArgumentsGetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + : FunctionObject(scope), index(index) { + setVTable(&static_vtbl); + } static ReturnedValue call(Managed *that, CallData *d); }; @@ -65,7 +67,9 @@ struct ArgumentsSetterFunction: FunctionObject uint index; ArgumentsSetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + : FunctionObject(scope), index(index) { + setVTable(&static_vtbl); + } static ReturnedValue call(Managed *that, CallData *callData); }; @@ -74,20 +78,26 @@ struct ArgumentsSetterFunction: FunctionObject struct ArgumentsObject: Object { Q_MANAGED CallContext *context; + bool fullyCreated; QVector<SafeValue> mappedArguments; ArgumentsObject(CallContext *context); ~ArgumentsObject() {} + enum { LengthPropertyIndex = 0, CalleePropertyIndex = 1, CallerPropertyIndex = 2 }; bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); - + static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static void putIndexed(Managed *m, uint index, const ValueRef value); + static bool deleteIndexedProperty(Managed *m, uint index); + static PropertyAttributes queryIndexed(const Managed *m, uint index); static void markObjects(Managed *that, ExecutionEngine *e); -protected: static void destroy(Managed *); + + void fullyCreate(); }; } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index a0f0345b8b..1628cfe4da 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -51,7 +51,7 @@ DEFINE_MANAGED_VTABLE(ArrayCtor); ArrayCtor::ArrayCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Array")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) @@ -65,7 +65,7 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) len = callData->args[0].asArrayLength(&ok); if (!ok) - return v4->current->throwRangeError(callData->args[0]); + return v4->currentContext()->throwRangeError(callData->args[0]); if (len < 0x1000) a->arrayReserve(len); @@ -174,12 +174,22 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx) } ScopedArrayObject elt(scope); + ScopedObject eltAsObj(scope); + ScopedValue entry(scope); for (int i = 0; i < ctx->callData->argc; ++i) { + eltAsObj = ctx->callData->args[i]; elt = ctx->callData->args[i]; - if (elt) + if (elt) { result->arrayConcat(elt.getPointer()); - else + } else if (eltAsObj && eltAsObj->isListType()) { + const uint startIndex = getLength(ctx, result); + for (int i = 0, len = getLength(ctx, eltAsObj); i < len; ++i) { + entry = eltAsObj->getIndexed(i); + result->putIndexed(startIndex + i, entry); + } + } else { result->arraySet(getLength(ctx, result), ctx->callData->args[i]); + } } return result.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index a0d0027e5f..f8edfb7850 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -49,7 +49,7 @@ DEFINE_MANAGED_VTABLE(BooleanObject); BooleanCtor::BooleanCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Boolean")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue BooleanCtor::construct(Managed *m, CallData *callData) diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 56c00f99ed..8d6adc0fb2 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -39,7 +39,7 @@ ** ****************************************************************************/ #ifndef QV4BOOLEANOBJECT_H -#define QBOOLEANOBJECT_H +#define QV4BOOLEANOBJECT_H #include "qv4object_p.h" #include "qv4functionobject_p.h" diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 97247ad368..05a0e66e09 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -51,19 +51,37 @@ using namespace QV4; +const ManagedVTable ExecutionContext::static_vtbl = +{ + call, + construct, + markObjects, + destroy, + 0 /*collectDeletables*/, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + isEqualTo, + 0, + "ExecutionContext", +}; + CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData *callData) { - CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocContext(requiredMemoryForExecutionContect(function, callData->argc))); - - engine->current = c; - - c->initBaseContext(Type_CallContext, engine, this); + CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocManaged(requiredMemoryForExecutionContect(function, callData->argc))); + new (c) CallContext(engine, Type_CallContext); c->function = function; c->realArgumentCount = callData->argc; c->strictMode = function->strictMode; - c->marked = false; c->outer = function->scope; #ifndef QT_NO_DEBUG assert(c->outer->next != (ExecutionContext *)0x1); @@ -92,27 +110,20 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData WithContext *ExecutionContext::newWithContext(ObjectRef with) { - WithContext *w = static_cast<WithContext *>(engine->memoryManager->allocContext(sizeof(WithContext))); - engine->current = w; - w->initWithContext(this, with); + WithContext *w = new (engine->memoryManager) WithContext(engine, with); return w; } CatchContext *ExecutionContext::newCatchContext(const StringRef exceptionVarName, const ValueRef exceptionValue) { - CatchContext *c = static_cast<CatchContext *>(engine->memoryManager->allocContext(sizeof(CatchContext))); - engine->current = c; - c->initCatchContext(this, exceptionVarName, exceptionValue); + CatchContext *c = new (engine->memoryManager) CatchContext(engine, exceptionVarName, exceptionValue); return c; } CallContext *ExecutionContext::newQmlContext(FunctionObject *f, ObjectRef qml) { - CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0))); - - engine->current = c; - c->initQmlContext(this, qml, f); - + CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocManaged(requiredMemoryForExecutionContect(f, 0))); + new (c) CallContext(engine, qml, f); return c; } @@ -149,7 +160,7 @@ String * const *ExecutionContext::formals() const if (type < Type_SimpleCallContext) return 0; QV4::FunctionObject *f = static_cast<const CallContext *>(this)->function; - return f ? f->formalParameterList : 0; + return (f && f->function) ? f->function->internalClass->nameMap.constData() : 0; } unsigned int ExecutionContext::formalCount() const @@ -165,7 +176,7 @@ String * const *ExecutionContext::variables() const if (type < Type_SimpleCallContext) return 0; QV4::FunctionObject *f = static_cast<const CallContext *>(this)->function; - return f ? f->varList : 0; + return (f && f->function) ? f->function->internalClass->nameMap.constData() + f->function->nArguments : 0; } unsigned int ExecutionContext::variableCount() const @@ -177,53 +188,47 @@ unsigned int ExecutionContext::variableCount() const } -void GlobalContext::initGlobalContext(ExecutionEngine *eng) +GlobalContext::GlobalContext(ExecutionEngine *eng) + : ExecutionContext(eng, Type_GlobalContext) { - initBaseContext(Type_GlobalContext, eng, /*parentContext*/0); - callData = reinterpret_cast<CallData *>(this + 1); - callData->tag = QV4::Value::_Integer_Type; - callData->argc = 0; - callData->thisObject = eng->globalObject; - global = 0; + global = eng->globalObject; } -void WithContext::initWithContext(ExecutionContext *p, ObjectRef with) +WithContext::WithContext(ExecutionEngine *engine, ObjectRef with) + : ExecutionContext(engine, Type_WithContext) { - initBaseContext(Type_WithContext, p->engine, p); - callData = p->callData; - outer = p; - lookups = p->lookups; - compilationUnit = p->compilationUnit; + callData = parent->callData; + outer = parent; + lookups = parent->lookups; + compilationUnit = parent->compilationUnit; withObject = with.getPointer(); } -void CatchContext::initCatchContext(ExecutionContext *p, const StringRef exceptionVarName, const ValueRef exceptionValue) +CatchContext::CatchContext(ExecutionEngine *engine, const StringRef exceptionVarName, const ValueRef exceptionValue) + : ExecutionContext(engine, Type_CatchContext) { - initBaseContext(Type_CatchContext, p->engine, p); - strictMode = p->strictMode; - callData = p->callData; - outer = p; - lookups = p->lookups; - compilationUnit = p->compilationUnit; + strictMode = parent->strictMode; + callData = parent->callData; + outer = parent; + lookups = parent->lookups; + compilationUnit = parent->compilationUnit; this->exceptionVarName = exceptionVarName; this->exceptionValue = exceptionValue; } -void CallContext::initQmlContext(ExecutionContext *parentContext, ObjectRef qml, FunctionObject *function) +CallContext::CallContext(ExecutionEngine *engine, ObjectRef qml, FunctionObject *function) + : ExecutionContext(engine, Type_QmlContext) { - initBaseContext(Type_QmlContext, parentContext->engine, parentContext); - this->function = function; - this->callData = reinterpret_cast<CallData *>(this + 1); - this->callData->tag = QV4::Value::_Integer_Type; - this->callData->argc = 0; - this->callData->thisObject = Primitive::undefinedValue(); + callData = reinterpret_cast<CallData *>(this + 1); + callData->tag = QV4::Value::_Integer_Type; + callData->argc = 0; + callData->thisObject = Primitive::undefinedValue(); strictMode = true; - marked = false; - this->outer = function->scope; + outer = function->scope; #ifndef QT_NO_DEBUG assert(outer->next != (ExecutionContext *)0x1); #endif @@ -259,12 +264,10 @@ bool ExecutionContext::deleteProperty(const StringRef name) CallContext *c = static_cast<CallContext *>(ctx); FunctionObject *f = c->function; if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return false; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return false; + uint index = f->function->internalClass->find(name); + if (index < UINT_MAX) + // ### throw in strict mode? + return false; } if (c->activation && c->activation->__hasProperty__(name)) return c->activation->deleteProperty(name); @@ -285,36 +288,34 @@ bool CallContext::needsOwnArguments() const return function->needsActivation || callData->argc < static_cast<int>(function->formalParameterCount); } -void ExecutionContext::mark() +void ExecutionContext::markObjects(Managed *m, ExecutionEngine *engine) { - if (marked) - return; - marked = true; + ExecutionContext *ctx = static_cast<ExecutionContext *>(m); - if (outer) - outer->mark(); + if (ctx->outer) + ctx->outer->mark(engine); // ### shouldn't need these 3 lines - callData->thisObject.mark(engine); - for (int arg = 0; arg < callData->argc; ++arg) - callData->args[arg].mark(engine); + ctx->callData->thisObject.mark(engine); + for (int arg = 0; arg < ctx->callData->argc; ++arg) + ctx->callData->args[arg].mark(engine); - if (type >= Type_CallContext) { - QV4::CallContext *c = static_cast<CallContext *>(this); + if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast<CallContext *>(ctx); for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) c->locals[local].mark(engine); if (c->activation) c->activation->mark(engine); c->function->mark(engine); - } else if (type == Type_WithContext) { - WithContext *w = static_cast<WithContext *>(this); + } else if (ctx->type == Type_WithContext) { + WithContext *w = static_cast<WithContext *>(ctx); w->withObject->mark(engine); - } else if (type == Type_CatchContext) { - CatchContext *c = static_cast<CatchContext *>(this); + } else if (ctx->type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(ctx); c->exceptionVarName->mark(engine); c->exceptionValue.mark(engine); - } else if (type == Type_GlobalContext) { - GlobalContext *g = static_cast<GlobalContext *>(this); + } else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); g->global->mark(engine); } } @@ -336,24 +337,35 @@ void ExecutionContext::setProperty(const StringRef name, const ValueRef value) ScopedObject activation(scope, (Object *)0); if (ctx->type >= Type_CallContext) { CallContext *c = static_cast<CallContext *>(ctx); - for (unsigned int i = 0; i < c->function->varCount; ++i) - if (c->function->varList[i]->isEqualTo(name)) { - c->locals[i] = *value; - return; - } - for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i) - if (c->function->formalParameterList[i]->isEqualTo(name)) { - c->callData->args[i] = *value; + if (c->function->function) { + uint index = c->function->function->internalClass->find(name); + if (index < UINT_MAX) { + if (index < c->function->formalParameterCount) { + c->callData->args[c->function->formalParameterCount - index - 1] = *value; + } else { + index -= c->function->formalParameterCount; + c->locals[index] = *value; + } return; } + } activation = c->activation; } else if (ctx->type == Type_GlobalContext) { activation = static_cast<GlobalContext *>(ctx)->global; } - if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) { - activation->put(name, value); - return; + if (activation) { + if (ctx->type == Type_QmlContext) { + activation->put(name, value); + return; + } else { + PropertyAttributes attrs; + Property *p = activation->__getOwnProperty__(name, &attrs); + if (p) { + activation->putValue(p, attrs, value); + return; + } + } } } } @@ -398,13 +410,13 @@ ReturnedValue ExecutionContext::getProperty(const StringRef name) else if (ctx->type >= Type_CallContext) { QV4::CallContext *c = static_cast<CallContext *>(ctx); ScopedFunctionObject f(scope, c->function); - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i].asReturnedValue(); - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->callData->args[i].asReturnedValue(); + if (f->function && (f->needsActivation || hasWith || hasCatchScope)) { + uint index = f->function->internalClass->find(name); + if (index < UINT_MAX) { + if (index < c->function->formalParameterCount) + return c->callData->args[c->function->formalParameterCount - index - 1].asReturnedValue(); + return c->locals[index - c->function->formalParameterCount].asReturnedValue(); + } } if (c->activation) { bool hasProperty = false; @@ -464,13 +476,13 @@ ReturnedValue ExecutionContext::getPropertyAndBase(const StringRef name, ObjectR else if (ctx->type >= Type_CallContext) { QV4::CallContext *c = static_cast<CallContext *>(ctx); FunctionObject *f = c->function; - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i].asReturnedValue(); - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->callData->args[i].asReturnedValue(); + if (f->function && (f->needsActivation || hasWith || hasCatchScope)) { + uint index = f->function->internalClass->find(name); + if (index < UINT_MAX) { + if (index < c->function->formalParameterCount) + return c->callData->args[c->function->formalParameterCount - index - 1].asReturnedValue(); + return c->locals[index - c->function->formalParameterCount].asReturnedValue(); + } } if (c->activation) { bool hasProperty = false; diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index ccb5cf98f8..4eb89ad905 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -44,6 +44,7 @@ #include "qv4global_p.h" #include "qv4value_def_p.h" #include "qv4managed_p.h" +#include "qv4engine_p.h" QT_BEGIN_NAMESPACE @@ -66,9 +67,11 @@ struct CallContext; struct CatchContext; struct WithContext; -struct Q_QML_EXPORT ExecutionContext +struct Q_QML_EXPORT ExecutionContext : public Managed { - enum Type { + Q_MANAGED + + enum ContextType { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, Type_WithContext = 0x3, @@ -77,9 +80,24 @@ struct Q_QML_EXPORT ExecutionContext Type_QmlContext = 0x6 }; - Type type; + ExecutionContext(ExecutionEngine *engine, ContextType t) + : Managed(engine->executionContextClass) + { + this->type = t; + strictMode = false; + this->engine = engine; + this->parent = engine->currentContext(); + outer = 0; + lookups = 0; + compilationUnit = 0; + currentEvalCode = 0; + interpreterInstructionPointer = 0; + lineNumber = -1; + engine->current = this; + } + + ContextType type; bool strictMode; - bool marked; CallData *callData; @@ -98,28 +116,14 @@ struct Q_QML_EXPORT ExecutionContext EvalCode *currentEvalCode; const uchar **interpreterInstructionPointer; - char *jitInstructionPointer; - - void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext) - { - this->type = type; - strictMode = false; - marked = false; - this->engine = engine; - parent = parentContext; - outer = 0; - lookups = 0; - compilationUnit = 0; - currentEvalCode = 0; - interpreterInstructionPointer = 0; - jitInstructionPointer = 0; - } + int lineNumber; CallContext *newCallContext(FunctionObject *f, CallData *callData); WithContext *newWithContext(ObjectRef with); CatchContext *newCatchContext(const StringRef exceptionVarName, const ValueRef exceptionValue); CallContext *newQmlContext(FunctionObject *f, ObjectRef qml); + // formals are in reverse order String * const *formals() const; unsigned int formalCount() const; String * const *variables() const; @@ -130,11 +134,11 @@ struct Q_QML_EXPORT ExecutionContext ReturnedValue throwError(const QV4::ValueRef value); ReturnedValue throwError(const QString &message); ReturnedValue throwSyntaxError(const QString &message); - ReturnedValue throwSyntaxError(const QString &message, const QString &fileName, int line, int column); + ReturnedValue throwSyntaxError(const QString &message, const QString &fileName, int lineNumber, int column); ReturnedValue throwTypeError(); ReturnedValue throwTypeError(const QString &message); ReturnedValue throwReferenceError(const ValueRef value); - ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int line, int column); + ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column); ReturnedValue throwRangeError(const ValueRef value); ReturnedValue throwRangeError(const QString &message); ReturnedValue throwURIError(const ValueRef msg); @@ -148,26 +152,27 @@ struct Q_QML_EXPORT ExecutionContext // Can only be called from within catch(...), rethrows if no JS exception. ReturnedValue catchException(StackTrace *trace = 0); - void mark(); - inline CallContext *asCallContext(); inline const CallContext *asCallContext() const; + + static void markObjects(Managed *m, ExecutionEngine *e); }; struct CallContext : public ExecutionContext { - FunctionObject *function; - int realArgumentCount; - SafeValue *locals; - Object *activation; - - void initSimpleCallContext(ExecutionEngine *engine, ExecutionContext *parent) { - initBaseContext(Type_SimpleCallContext, engine, parent); + CallContext(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) + : ExecutionContext(engine, t) + { function = 0; locals = 0; activation = 0; } - void initQmlContext(ExecutionContext *parentContext, ObjectRef qml, QV4::FunctionObject *function); + CallContext(ExecutionEngine *engine, ObjectRef qml, QV4::FunctionObject *function); + + FunctionObject *function; + int realArgumentCount; + SafeValue *locals; + Object *activation; inline ReturnedValue argument(int i); bool needsOwnArguments() const; @@ -175,14 +180,14 @@ struct CallContext : public ExecutionContext struct GlobalContext : public ExecutionContext { - void initGlobalContext(ExecutionEngine *e); + GlobalContext(ExecutionEngine *engine); Object *global; }; struct CatchContext : public ExecutionContext { - void initCatchContext(ExecutionContext *p, const StringRef exceptionVarName, const ValueRef exceptionValue); + CatchContext(ExecutionEngine *engine, const StringRef exceptionVarName, const ValueRef exceptionValue); SafeString exceptionVarName; SafeValue exceptionValue; @@ -190,9 +195,8 @@ struct CatchContext : public ExecutionContext struct WithContext : public ExecutionContext { + WithContext(ExecutionEngine *engine, ObjectRef with); Object *withObject; - - void initWithContext(ExecutionContext *p, ObjectRef with); }; inline CallContext *ExecutionContext::asCallContext() @@ -205,6 +209,37 @@ inline const CallContext *ExecutionContext::asCallContext() const return type >= Type_SimpleCallContext ? static_cast<const CallContext *>(this) : 0; } + +inline void ExecutionEngine::pushContext(CallContext *context) +{ + context->parent = current; + current = context; + current->currentEvalCode = 0; +} + +inline ExecutionContext *ExecutionEngine::popContext() +{ + Q_ASSERT(current->parent); + current = current->parent; + return current; +} + +struct ExecutionContextSaver +{ + ExecutionEngine *engine; + ExecutionContext *savedContext; + + ExecutionContextSaver(ExecutionContext *context) + : engine(context->engine) + , savedContext(context) + { + } + ~ExecutionContextSaver() + { + engine->current = savedContext; + } +}; + /* Function *f, int argc */ #define requiredMemoryForExecutionContect(f, argc) \ sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) + sizeof(CallData) diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index b732c8a04a..5d0c8ccf8e 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -646,7 +646,7 @@ DEFINE_MANAGED_VTABLE(DateObject); DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date) : Object(engine->dateClass) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_DateObject; value.setDouble(date.isValid() ? date.toMSecsSinceEpoch() : qSNaN()); } @@ -661,7 +661,7 @@ DEFINE_MANAGED_VTABLE(DateCtor); DateCtor::DateCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Date")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue DateCtor::construct(Managed *m, CallData *callData) diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index e96cac2f20..9c451dd251 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -55,7 +55,6 @@ struct DateObject: Object { Q_MANAGED SafeValue value; DateObject(ExecutionEngine *engine, const ValueRef date): Object(engine->dateClass) { - vtbl = &static_vtbl; type = Type_DateObject; value = date; } @@ -65,7 +64,7 @@ struct DateObject: Object { protected: DateObject(InternalClass *ic): Object(ic) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_DateObject; value = Primitive::fromDouble(qSNaN()); } diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index 95b4100651..4170b6817f 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -185,7 +185,7 @@ void Debugger::resume(Speed speed) if (speed == StepOver) setTemporaryBreakPointOnNextLine(); if (speed == StepOut) - m_temporaryBreakPoints = TemporaryBreakPoint(getFunction(), m_engine->current); + m_temporaryBreakPoints = TemporaryBreakPoint(getFunction(), m_engine->currentContext()); m_stepping = speed; m_runningCondition.wakeAll(); @@ -293,15 +293,16 @@ void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int if (frameNr < 0) return; - CallContext *ctxt = findScope(findContext(engine->current, frameNr), scopeNr); + CallContext *ctxt = findScope(findContext(engine->currentContext(), frameNr), scopeNr); if (!ctxt) return; Scope scope(engine); ScopedValue v(scope); - for (unsigned i = 0, ei = ctxt->formalCount(); i != ei; ++i) { + int nFormals = ctxt->formalCount(); + for (unsigned i = 0, ei = nFormals; i != ei; ++i) { QString qName; - if (String *name = ctxt->formals()[i]) + if (String *name = ctxt->formals()[nFormals - i - 1]) qName = name->toQString(); v = ctxt->argument(i); collector->collect(qName, v); @@ -339,7 +340,7 @@ void Debugger::collectLocalsInContext(Collector *collector, int frameNr, int sco if (frameNr < 0) return; - CallContext *ctxt = findScope(findContext(engine->current, frameNr), scopeNr); + CallContext *ctxt = findScope(findContext(engine->currentContext(), frameNr), scopeNr); if (!ctxt) return; @@ -386,7 +387,7 @@ bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame) bool myRun() { - ExecutionContext *ctxt = findContext(engine->current, frameNr); + ExecutionContext *ctxt = findContext(engine->currentContext(), frameNr); while (ctxt) { if (CallContext *cCtxt = ctxt->asCallContext()) if (cCtxt->activation) @@ -448,14 +449,14 @@ void Debugger::collectReturnedValue(Collector *collector) const collector->collect(o); } -QVector<ExecutionContext::Type> Debugger::getScopeTypes(int frame) const +QVector<ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const { - QVector<ExecutionContext::Type> types; + QVector<ExecutionContext::ContextType> types; if (state() != Paused) return types; - CallContext *sctxt = findContext(m_engine->current, frame); + CallContext *sctxt = findContext(m_engine->currentContext(), frame); if (!sctxt || sctxt->type < ExecutionContext::Type_SimpleCallContext) return types; CallContext *ctxt = static_cast<CallContext *>(sctxt); @@ -499,7 +500,7 @@ void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit) m_pauseRequested = false; pauseAndWait(PauseRequest); } else if (breakPointHit) { - if (m_stepping == StepOver && m_temporaryBreakPoints.context == m_engine->current) + if (m_stepping == StepOver && m_temporaryBreakPoints.context == m_engine->currentContext()) pauseAndWait(Step); else if (reallyHitTheBreakPoint(state.fileName, state.lineNumber)) pauseAndWait(BreakPoint); @@ -527,7 +528,7 @@ void Debugger::leavingFunction(const ReturnedValue &retVal) QMutexLocker locker(&m_lock); if ((m_stepping == StepOut || m_stepping == StepOver) - && temporaryBreakPointInFunction(m_engine->current)) { + && temporaryBreakPointInFunction(m_engine->currentContext())) { clearTemporaryBreakPoints(); m_stepping = NotStepping; m_stopForStepping = true; @@ -551,7 +552,7 @@ void Debugger::aboutToThrow() Function *Debugger::getFunction() const { - ExecutionContext *context = m_engine->current; + ExecutionContext *context = m_engine->currentContext(); if (CallContext *callCtx = context->asCallContext()) return callCtx->function->function; else { @@ -594,7 +595,7 @@ void Debugger::setTemporaryBreakPointOnNextLine() if (pcs.isEmpty()) return; - m_temporaryBreakPoints = TemporaryBreakPoint(function, m_engine->current); + m_temporaryBreakPoints = TemporaryBreakPoint(function, m_engine->currentContext()); m_temporaryBreakPoints.codeOffsets.reserve(pcs.size()); for (QList<qptrdiff>::const_iterator i = pcs.begin(), ei = pcs.end(); i != ei; ++i) { // note: we do set a breakpoint on the current line, because there could be a loop where diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 98b549995e..0e19c51935 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -163,7 +163,7 @@ public: bool collectThisInContext(Collector *collector, int frame = 0); void collectThrownValue(Collector *collector); void collectReturnedValue(Collector *collector) const; - QVector<ExecutionContext::Type> getScopeTypes(int frame = 0) const; + QVector<ExecutionContext::ContextType> getScopeTypes(int frame = 0) const; public: // compile-time interface void maybeBreakAtInstruction(const uchar *code, bool breakPointHit); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 8cd059dd2b..dc8c0da321 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ #include <qv4engine_p.h> +#include <qv4context_p.h> #include <qv4value_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> @@ -52,6 +53,7 @@ #include <qv4mathobject_p.h> #include <qv4numberobject_p.h> #include <qv4regexpobject_p.h> +#include <qv4regexp_p.h> #include <qv4variantobject_p.h> #include <qv4runtime_p.h> #include "qv4mm_p.h" @@ -134,11 +136,13 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) : memoryManager(new QV4::MemoryManager) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) + , current(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) , debugger(0) , globalObject(0) , globalCode(0) + , v8Engine(0) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) @@ -181,6 +185,9 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) identifierTable = new IdentifierTable(this); emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this); + executionContextClass = emptyClass->changeVTable(&ExecutionContext::static_vtbl); + stringClass = emptyClass->changeVTable(&String::static_vtbl); + regExpValueClass = emptyClass->changeVTable(&RegExp::static_vtbl); id_undefined = newIdentifier(QStringLiteral("undefined")); id_null = newIdentifier(QStringLiteral("null")); @@ -213,33 +220,39 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) id_toString = newIdentifier(QStringLiteral("toString")); id_valueOf = newIdentifier(QStringLiteral("valueOf")); - ObjectPrototype *objectPrototype = new (memoryManager) ObjectPrototype(emptyClass); - objectClass = emptyClass->changePrototype(objectPrototype); + ObjectPrototype *objectPrototype = new (memoryManager) ObjectPrototype(emptyClass->changeVTable(&ObjectPrototype::static_vtbl)); + objectClass = InternalClass::create(this, &Object::static_vtbl, objectPrototype); + Q_ASSERT(objectClass->vtable == &Object::static_vtbl); - arrayClass = objectClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); + arrayClass = InternalClass::create(this, &ArrayObject::static_vtbl, objectPrototype); + arrayClass = arrayClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); ArrayPrototype *arrayPrototype = new (memoryManager) ArrayPrototype(arrayClass); arrayClass = arrayClass->changePrototype(arrayPrototype); - InternalClass *argsClass = objectClass->addMember(id_length, Attr_NotEnumerable); + InternalClass *argsClass = InternalClass::create(this, &ArgumentsObject::static_vtbl, objectPrototype); + argsClass = argsClass->addMember(id_length, Attr_NotEnumerable); argumentsObjectClass = argsClass->addMember(id_callee, Attr_Data|Attr_NotEnumerable); strictArgumentsObjectClass = argsClass->addMember(id_callee, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + Q_ASSERT(argumentsObjectClass->vtable == &ArgumentsObject::static_vtbl); + Q_ASSERT(strictArgumentsObjectClass->vtable == &ArgumentsObject::static_vtbl); + initRootContext(); StringPrototype *stringPrototype = new (memoryManager) StringPrototype(objectClass); - stringClass = emptyClass->changePrototype(stringPrototype); + stringObjectClass = InternalClass::create(this, &String::static_vtbl, stringPrototype); NumberPrototype *numberPrototype = new (memoryManager) NumberPrototype(objectClass); - numberClass = emptyClass->changePrototype(numberPrototype); + numberClass = InternalClass::create(this, &NumberObject::static_vtbl, numberPrototype); BooleanPrototype *booleanPrototype = new (memoryManager) BooleanPrototype(objectClass); - booleanClass = emptyClass->changePrototype(booleanPrototype); + booleanClass = InternalClass::create(this, &BooleanObject::static_vtbl, booleanPrototype); DatePrototype *datePrototype = new (memoryManager) DatePrototype(objectClass); - dateClass = emptyClass->changePrototype(datePrototype); + dateClass = InternalClass::create(this, &DateObject::static_vtbl, datePrototype); - FunctionPrototype *functionPrototype = new (memoryManager) FunctionPrototype(objectClass); - functionClass = emptyClass->changePrototype(functionPrototype); + FunctionPrototype *functionPrototype = new (memoryManager) FunctionPrototype(InternalClass::create(this, &FunctionPrototype::static_vtbl, objectPrototype)); + functionClass = InternalClass::create(this, &FunctionObject::static_vtbl, functionPrototype); uint index; functionWithProtoClass = functionClass->addMember(id_prototype, Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == FunctionObject::Index_Prototype); @@ -247,32 +260,33 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) Q_ASSERT(index == FunctionObject::Index_ProtoConstructor); RegExpPrototype *regExpPrototype = new (memoryManager) RegExpPrototype(objectClass); - regExpClass = emptyClass->changePrototype(regExpPrototype); + regExpClass = InternalClass::create(this, &RegExpObject::static_vtbl, regExpPrototype); regExpExecArrayClass = arrayClass->addMember(id_index, Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); regExpExecArrayClass = regExpExecArrayClass->addMember(id_input, Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); ErrorPrototype *errorPrototype = new (memoryManager) ErrorPrototype(objectClass); - errorClass = emptyClass->changePrototype(errorPrototype); + errorClass = InternalClass::create(this, &ErrorObject::static_vtbl, errorPrototype); EvalErrorPrototype *evalErrorPrototype = new (memoryManager) EvalErrorPrototype(errorClass); - evalErrorClass = emptyClass->changePrototype(evalErrorPrototype); + evalErrorClass = InternalClass::create(this, &EvalErrorObject::static_vtbl, evalErrorPrototype); RangeErrorPrototype *rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(errorClass); - rangeErrorClass = emptyClass->changePrototype(rangeErrorPrototype); + rangeErrorClass = InternalClass::create(this, &RangeErrorObject::static_vtbl, rangeErrorPrototype); ReferenceErrorPrototype *referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(errorClass); - referenceErrorClass = emptyClass->changePrototype(referenceErrorPrototype); + referenceErrorClass = InternalClass::create(this, &ReferenceErrorObject::static_vtbl, referenceErrorPrototype); SyntaxErrorPrototype *syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(errorClass); - syntaxErrorClass = emptyClass->changePrototype(syntaxErrorPrototype); + syntaxErrorClass = InternalClass::create(this, &SyntaxErrorObject::static_vtbl, syntaxErrorPrototype); TypeErrorPrototype *typeErrorPrototype = new (memoryManager) TypeErrorPrototype(errorClass); - typeErrorClass = emptyClass->changePrototype(typeErrorPrototype); + typeErrorClass = InternalClass::create(this, &TypeErrorObject::static_vtbl, typeErrorPrototype); URIErrorPrototype *uRIErrorPrototype = new (memoryManager) URIErrorPrototype(errorClass); - uriErrorClass = emptyClass->changePrototype(uRIErrorPrototype); + uriErrorClass = InternalClass::create(this, &URIErrorObject::static_vtbl, uRIErrorPrototype); - VariantPrototype *variantPrototype = new (memoryManager) VariantPrototype(objectClass); - variantClass = emptyClass->changePrototype(variantPrototype); + VariantPrototype *variantPrototype = new (memoryManager) VariantPrototype(InternalClass::create(this, &VariantPrototype::static_vtbl, objectPrototype)); + variantClass = InternalClass::create(this, &VariantObject::static_vtbl, variantPrototype); + Q_ASSERT(variantClass->prototype == variantPrototype); + Q_ASSERT(variantPrototype->internalClass->prototype == objectPrototype); - SequencePrototype *sequencePrototype = new (memoryManager) SequencePrototype(arrayClass->changePrototype(arrayPrototype)); - sequenceClass = emptyClass->changePrototype(sequencePrototype); + sequencePrototype = new (memoryManager) SequencePrototype(arrayClass); objectCtor = new (memoryManager) ObjectCtor(rootContext); stringCtor = new (memoryManager) StringCtor(rootContext); @@ -307,7 +321,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) uRIErrorPrototype->init(this, uRIErrorCtor); variantPrototype->init(); - sequencePrototype->init(); + static_cast<SequencePrototype *>(sequencePrototype.managed())->init(); // // set up the global object @@ -315,6 +329,7 @@ ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) globalObject = newObject()->getPointer(); rootContext->global = globalObject; rootContext->callData->thisObject = globalObject; + Q_ASSERT(globalObject->internalClass->vtable); globalObject->defineDefaultProperty(QStringLiteral("Object"), objectCtor); globalObject->defineDefaultProperty(QStringLiteral("String"), stringCtor); @@ -389,10 +404,13 @@ void ExecutionEngine::enableDebugger() void ExecutionEngine::initRootContext() { - rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext) + sizeof(CallData))); - current = rootContext; - current->parent = 0; - rootContext->initGlobalContext(this); + rootContext = static_cast<GlobalContext *>(memoryManager->allocManaged(sizeof(GlobalContext) + sizeof(CallData))); + new (rootContext) GlobalContext(this); + rootContext->callData = reinterpret_cast<CallData *>(rootContext + 1); + rootContext->callData->tag = QV4::Value::_Integer_Type; + rootContext->callData->argc = 0; + rootContext->callData->thisObject = globalObject; + rootContext->callData->args[0] = Encode::undefined(); } InternalClass *ExecutionEngine::newClass(const InternalClass &other) @@ -402,14 +420,11 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other) ExecutionContext *ExecutionEngine::pushGlobalContext() { - GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); - ExecutionContext *oldNext = g->next; - *g = *rootContext; - g->next = oldNext; - g->parent = current; - current = g; + GlobalContext *g = new (memoryManager) GlobalContext(this); + g->callData = rootContext->callData; - return current; + Q_ASSERT(currentContext() == g); + return g; } Returned<FunctionObject> *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, const StringRef name, ReturnedValue (*code)(CallContext *)) @@ -597,7 +612,7 @@ Returned<Object> *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ct Returned<Object> *ExecutionEngine::qmlContextObject() const { - ExecutionContext *ctx = current; + ExecutionContext *ctx = currentContext(); if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext && !ctx->outer) ctx = ctx->parent; @@ -627,11 +642,12 @@ namespace { void resolve(StackFrame *frame, ExecutionContext *context, Function *function) { qptrdiff offset; - if (context->interpreterInstructionPointer) + if (context->interpreterInstructionPointer) { offset = *context->interpreterInstructionPointer - 1 - function->codeData; - else - offset = context->jitInstructionPointer - (char*)function->codePtr; - frame->line = function->lineNumberForProgramCounter(offset); + frame->line = function->lineNumberForProgramCounter(offset); + } else { + frame->line = context->lineNumber; + } } }; } @@ -642,7 +658,7 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const QVector<StackFrame> stack; - QV4::ExecutionContext *c = current; + QV4::ExecutionContext *c = currentContext(); while (c && frameLimit) { CallContext *callCtx = c->asCallContext(); if (callCtx && callCtx->function) { @@ -696,7 +712,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) return src; QUrl base; - QV4::ExecutionContext *c = current; + QV4::ExecutionContext *c = currentContext(); while (c) { CallContext *callCtx = c->asCallContext(); if (callCtx && callCtx->function) { @@ -752,9 +768,9 @@ void ExecutionEngine::markObjects() setter->mark(this); } - ExecutionContext *c = current; + ExecutionContext *c = currentContext(); while (c) { - c->mark(); + c->mark(this); c = c->parent; } @@ -794,6 +810,7 @@ void ExecutionEngine::markObjects() syntaxErrorCtor.mark(this); typeErrorCtor.mark(this); uRIErrorCtor.mark(this); + sequencePrototype.mark(this); exceptionValue.mark(this); @@ -814,13 +831,13 @@ namespace { { bool operator()(Function *function, quintptr pc) { - return reinterpret_cast<quintptr>(function->codePtr) < pc - && (reinterpret_cast<quintptr>(function->codePtr) + function->codeSize) < pc; + return reinterpret_cast<quintptr>(function->code) < pc + && (reinterpret_cast<quintptr>(function->code) + function->codeSize) < pc; } bool operator()(quintptr pc, Function *function) { - return pc < reinterpret_cast<quintptr>(function->codePtr); + return pc < reinterpret_cast<quintptr>(function->code); } }; } @@ -887,8 +904,8 @@ ReturnedValue ExecutionEngine::throwException(const ValueRef value) ReturnedValue ExecutionEngine::catchException(ExecutionContext *catchingContext, StackTrace *trace) { Q_ASSERT(hasException); - while (current != catchingContext) - popContext(); + Q_UNUSED(catchingContext); + Q_ASSERT(currentContext() == catchingContext); if (trace) *trace = exceptionStackTrace; exceptionStackTrace.clear(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index b4972904ee..ecb5f2b4d5 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -44,7 +44,6 @@ #include "qv4global_p.h" #include "private/qv4isel_p.h" #include "qv4util_p.h" -#include "qv4context_p.h" #include "qv4property_p.h" #include <private/qintrusivelist_p.h> @@ -112,11 +111,12 @@ class RegExp; class RegExpCache; struct QmlExtensions; struct Exception; +struct ExecutionContextSaver; #define CHECK_STACK_LIMITS(v4) \ if ((v4->jsStackTop <= v4->jsStackLimit) && (reinterpret_cast<quintptr>(&v4) >= v4->cStackLimit || v4->recheckCStackLimits())) {} \ else \ - return v4->current->throwRangeError(QStringLiteral("Maximum call stack size exceeded.")) + return v4->currentContext()->throwRangeError(QStringLiteral("Maximum call stack size exceeded.")) struct Q_QML_EXPORT ExecutionEngine @@ -126,7 +126,13 @@ struct Q_QML_EXPORT ExecutionEngine ExecutableAllocator *regExpAllocator; QScopedPointer<QQmlJS::EvalISelFactory> iselFactory; +private: + friend struct ExecutionContextSaver; + friend struct ExecutionContext; ExecutionContext *current; +public: + ExecutionContext *currentContext() const { return current; } + GlobalContext *rootContext; SafeValue *jsStackTop; @@ -183,12 +189,16 @@ struct Q_QML_EXPORT ExecutionEngine SafeValue syntaxErrorCtor; SafeValue typeErrorCtor; SafeValue uRIErrorCtor; + SafeValue sequencePrototype; QQmlJS::MemoryPool classPool; InternalClass *emptyClass; + InternalClass *executionContextClass; + InternalClass *stringClass; + InternalClass *objectClass; InternalClass *arrayClass; - InternalClass *stringClass; + InternalClass *stringObjectClass; InternalClass *booleanClass; InternalClass *numberClass; InternalClass *dateClass; @@ -199,6 +209,7 @@ struct Q_QML_EXPORT ExecutionEngine InternalClass *regExpClass; InternalClass *regExpExecArrayClass; + InternalClass *regExpValueClass; InternalClass *errorClass; InternalClass *evalErrorClass; @@ -211,7 +222,6 @@ struct Q_QML_EXPORT ExecutionEngine InternalClass *strictArgumentsObjectClass; InternalClass *variantClass; - InternalClass *sequenceClass; EvalFunction *evalFunction; FunctionObject *thrower; @@ -356,38 +366,10 @@ private: QmlExtensions *m_qmlExtensions; }; -inline void ExecutionEngine::pushContext(CallContext *context) -{ - context->parent = current; - current = context; - current->currentEvalCode = 0; -} - -inline ExecutionContext *ExecutionEngine::popContext() -{ - current = current->parent; - return current; -} - -struct ExecutionContextSaver -{ - ExecutionEngine *engine; - ExecutionContext *savedContext; - - ExecutionContextSaver(ExecutionContext *context) - : engine(context->engine) - , savedContext(context) - { - } - ~ExecutionContextSaver() - { - engine->current = savedContext; - } -}; - inline void Managed::mark(QV4::ExecutionEngine *engine) { + Q_ASSERT(inUse); if (markBit) return; markBit = 1; diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index bac29d19e1..cf5c06dd41 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -77,7 +77,6 @@ ErrorObject::ErrorObject(InternalClass *ic) , stack(0) { type = Type_ErrorObject; - vtbl = &static_vtbl; Scope scope(engine()); ScopedValue protectThis(scope, this); @@ -91,7 +90,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const ValueRef message, ErrorType t) , stack(0) { type = Type_ErrorObject; - vtbl = &static_vtbl; subtype = t; Scope scope(engine()); @@ -116,7 +114,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const QString &message, ErrorObject: , stack(0) { type = Type_ErrorObject; - vtbl = &static_vtbl; subtype = t; Scope scope(engine()); @@ -141,7 +138,6 @@ ErrorObject::ErrorObject(InternalClass *ic, const QString &message, const QStrin , stack(0) { type = Type_ErrorObject; - vtbl = &static_vtbl; subtype = t; Scope scope(engine()); @@ -207,13 +203,11 @@ DEFINE_MANAGED_VTABLE(SyntaxErrorObject); SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const ValueRef msg) : ErrorObject(engine->syntaxErrorClass, msg, SyntaxError) { - vtbl = &static_vtbl; } SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber) : ErrorObject(engine->syntaxErrorClass, msg, fileName, lineNumber, columnNumber, SyntaxError) { - vtbl = &static_vtbl; } EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const ValueRef message) @@ -272,13 +266,13 @@ DEFINE_MANAGED_VTABLE(URIErrorCtor); ErrorCtor::ErrorCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Error")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ErrorCtor::ErrorCtor(ExecutionContext *scope, const QString &name) : FunctionObject(scope, name) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue ErrorCtor::construct(Managed *m, CallData *callData) @@ -296,7 +290,7 @@ ReturnedValue ErrorCtor::call(Managed *that, CallData *callData) EvalErrorCtor::EvalErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("EvalError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue EvalErrorCtor::construct(Managed *m, CallData *callData) @@ -309,7 +303,7 @@ ReturnedValue EvalErrorCtor::construct(Managed *m, CallData *callData) RangeErrorCtor::RangeErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("RangeError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue RangeErrorCtor::construct(Managed *m, CallData *callData) @@ -322,7 +316,7 @@ ReturnedValue RangeErrorCtor::construct(Managed *m, CallData *callData) ReferenceErrorCtor::ReferenceErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("ReferenceError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue ReferenceErrorCtor::construct(Managed *m, CallData *callData) @@ -335,7 +329,7 @@ ReturnedValue ReferenceErrorCtor::construct(Managed *m, CallData *callData) SyntaxErrorCtor::SyntaxErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("SyntaxError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue SyntaxErrorCtor::construct(Managed *m, CallData *callData) @@ -348,7 +342,7 @@ ReturnedValue SyntaxErrorCtor::construct(Managed *m, CallData *callData) TypeErrorCtor::TypeErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("TypeError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue TypeErrorCtor::construct(Managed *m, CallData *callData) @@ -361,7 +355,7 @@ ReturnedValue TypeErrorCtor::construct(Managed *m, CallData *callData) URIErrorCtor::URIErrorCtor(ExecutionContext *scope) : ErrorCtor(scope, QStringLiteral("URIError")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue URIErrorCtor::construct(Managed *m, CallData *callData) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 3f4cb8fc43..def776d3b6 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -184,37 +184,37 @@ struct ErrorPrototype: ErrorObject struct EvalErrorPrototype: ErrorObject { - EvalErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + EvalErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; struct RangeErrorPrototype: ErrorObject { - RangeErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + RangeErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; struct ReferenceErrorPrototype: ErrorObject { - ReferenceErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + ReferenceErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; struct SyntaxErrorPrototype: ErrorObject { - SyntaxErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + SyntaxErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; struct TypeErrorPrototype: ErrorObject { - TypeErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + TypeErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; struct URIErrorPrototype: ErrorObject { - URIErrorPrototype(InternalClass *ic): ErrorObject(ic) { vtbl = &static_vtbl; } + URIErrorPrototype(InternalClass *ic): ErrorObject(ic) { setVTable(&static_vtbl); } void init(ExecutionEngine *engine, ObjectRef ctor) { ErrorPrototype::init(engine, ctor, this); } }; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index ebe214ad72..0e90e213c4 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -54,7 +54,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, ReturnedValue (*codePtr)(ExecutionContext *, const uchar *), quint32 _codeSize) : compiledFunction(function) , compilationUnit(unit) - , codePtr(codePtr) + , code(codePtr) , codeData(0) , codeSize(_codeSize) { @@ -62,18 +62,29 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, name = compilationUnit->runtimeStrings[compiledFunction->nameIndex].asString(); - formals.resize(compiledFunction->nFormals); - formals.fill(0); - const quint32 *formalsIndices = compiledFunction->formalsTable(); - for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - formals[i] = compilationUnit->runtimeStrings[formalsIndices[i]].asString(); + nArguments = compiledFunction->nFormals; + internalClass = engine->emptyClass; + const quint32 *formalsIndices = compiledFunction->formalsTable(); + // iterate backwards, so we get the right ordering for duplicate names + for (int i = static_cast<int>(compiledFunction->nFormals - 1); i >= 0; --i) { + String *arg = compilationUnit->runtimeStrings[formalsIndices[i]].asString(); + while (1) { + InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); + if (newClass != internalClass) { + internalClass = newClass; + break; + } + // duplicate arguments, need some trick to store them + arg = new (engine->memoryManager) String(engine, arg, engine->newString(QString(0xfffe))->getPointer()); + } + } - locals.resize(compiledFunction->nLocals); - locals.fill(0); const quint32 *localsIndices = compiledFunction->localsTable(); - for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - locals[i] = compilationUnit->runtimeStrings[localsIndices[i]].asString(); + for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { + String *local = compilationUnit->runtimeStrings[localsIndices[i]].asString(); + internalClass = internalClass->addMember(local, Attr_NotConfigurable); + } } Function::~Function() @@ -84,10 +95,6 @@ Function::~Function() void Function::mark(ExecutionEngine *e) { name.mark(e); - for (int i = 0; i < formals.size(); ++i) - formals.at(i)->mark(e); - for (int i = 0; i < locals.size(); ++i) - locals.at(i)->mark(e); } namespace QV4 { diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 5d284f1b2b..8d07853b45 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -85,16 +85,14 @@ struct Function { const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; - inline ReturnedValue code(ExecutionContext *ctx, const uchar *data) { - return codePtr(ctx, data); - } - ReturnedValue (*codePtr)(ExecutionContext *, const uchar *); + ReturnedValue (*code)(ExecutionContext *, const uchar *); const uchar *codeData; quint32 codeSize; - QVector<String *> formals; - QVector<String *> locals; + // first nArguments names in internalClass are the actual arguments + int nArguments; + InternalClass *internalClass; Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, ReturnedValue (*codePtr)(ExecutionContext *, const uchar *), quint32 _codeSize); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index aa1cb89a44..6e5c137e0b 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -55,6 +55,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlcontextwrapper_p.h> +#include <private/qqmlengine_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" @@ -75,11 +76,11 @@ DEFINE_MANAGED_VTABLE(FunctionObject); FunctionObject::FunctionObject(ExecutionContext *scope, const StringRef name, bool createProto) : Object(createProto ? scope->engine->functionWithProtoClass : scope->engine->functionClass) , scope(scope) - , formalParameterList(0) - , varList(0) , formalParameterCount(0) , varCount(0) , function(0) + , protoCacheClass(0) + , protoCacheIndex(UINT_MAX) { init(name, createProto); } @@ -87,11 +88,11 @@ FunctionObject::FunctionObject(ExecutionContext *scope, const StringRef name, bo FunctionObject::FunctionObject(ExecutionContext *scope, const QString &name, bool createProto) : Object(createProto ? scope->engine->functionWithProtoClass : scope->engine->functionClass) , scope(scope) - , formalParameterList(0) - , varList(0) , formalParameterCount(0) , varCount(0) , function(0) + , protoCacheClass(0) + , protoCacheIndex(UINT_MAX) { // set the name to something here, so that a gc run a few lines below doesn't crash on it this->name = scope->engine->id_undefined; @@ -105,13 +106,10 @@ FunctionObject::FunctionObject(ExecutionContext *scope, const QString &name, boo FunctionObject::FunctionObject(InternalClass *ic) : Object(ic) , scope(ic->engine->rootContext) - , formalParameterList(0) - , varList(0) , formalParameterCount(0) , varCount(0) , function(0) { - vtbl = &static_vtbl; name = ic->engine->id_undefined; type = Type_FunctionObject; @@ -127,7 +125,6 @@ FunctionObject::~FunctionObject() void FunctionObject::init(const StringRef n, bool createProto) { - vtbl = &static_vtbl; name = n; Scope s(internalClass->engine); @@ -157,43 +154,13 @@ ReturnedValue FunctionObject::newInstance() return construct(callData); } -bool FunctionObject::hasInstance(Managed *that, const ValueRef value) -{ - Scope scope(that->internalClass->engine); - ScopedFunctionObject f(scope, static_cast<FunctionObject *>(that)); - - ScopedObject v(scope, value); - if (!v) - return false; - - Scoped<Object> o(scope, f->get(scope.engine->id_prototype)); - if (!o) { - scope.engine->current->throwTypeError(); - return false; - } - - while (v) { - v = v->prototype(); - - if (! v) - break; - else if (o.getPointer() == v) - return true; - } - - return false; -} - ReturnedValue FunctionObject::construct(Managed *that, CallData *) { ExecutionEngine *v4 = that->internalClass->engine; Scope scope(v4); Scoped<FunctionObject> f(scope, that, Scoped<FunctionObject>::Cast); - InternalClass *ic = v4->objectClass; - Scoped<Object> proto(scope, f->get(v4->id_prototype)); - if (!!proto) - ic = v4->emptyClass->changePrototype(proto.getPointer()); + InternalClass *ic = f->internalClassForConstructor(); Scoped<Object> obj(scope, v4->newObject(ic)); return obj.asReturnedValue(); } @@ -213,7 +180,7 @@ void FunctionObject::markObjects(Managed *that, ExecutionEngine *e) // formalParameterList[i]->mark(); // for (uint i = 0; i < varCount; ++i) // varList[i]->mark(); - o->scope->mark(); + o->scope->mark(e); if (o->function) o->function->mark(e); @@ -230,13 +197,49 @@ FunctionObject *FunctionObject::creatScriptFunction(ExecutionContext *scope, Fun return new (scope->engine->memoryManager) SimpleScriptFunction(scope, function); } +ReturnedValue FunctionObject::protoProperty() +{ + if (protoCacheClass != internalClass) { + protoCacheClass = internalClass; + protoCacheIndex = internalClass->find(internalClass->engine->id_prototype); + } + if (protoCacheIndex < UINT_MAX) { + if (internalClass->propertyData.at(protoCacheIndex).isData()) { + ReturnedValue v = memberData[protoCacheIndex].value.asReturnedValue(); + if (v != protoValue) { + classForConstructor = 0; + protoValue = v; + } + return v; + } + } + classForConstructor = 0; + return get(internalClass->engine->id_prototype); +} + +InternalClass *FunctionObject::internalClassForConstructor() +{ + // need to call this first to ensure we don't use a wrong class + ReturnedValue proto = protoProperty(); + if (classForConstructor) + return classForConstructor; + + Scope scope(internalClass->engine); + ScopedObject p(scope, proto); + if (p) + classForConstructor = InternalClass::create(scope.engine, &Object::static_vtbl, p.getPointer()); + else + classForConstructor = scope.engine->objectClass; + + return classForConstructor; +} DEFINE_MANAGED_VTABLE(FunctionCtor); FunctionCtor::FunctionCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Function")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } // 15.3.2 @@ -244,7 +247,7 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) { FunctionCtor *f = static_cast<FunctionCtor *>(that); ExecutionEngine *v4 = f->internalClass->engine; - ExecutionContext *ctx = v4->current; + ExecutionContext *ctx = v4->currentContext(); QString arguments; QString body; if (callData->argc > 0) { @@ -268,20 +271,20 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) const bool parsed = parser.parseExpression(); if (!parsed) - return v4->current->throwSyntaxError(QLatin1String("Parse error")); + return v4->currentContext()->throwSyntaxError(QLatin1String("Parse error")); using namespace QQmlJS::AST; FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode()); if (!fe) - return v4->current->throwSyntaxError(QLatin1String("Parse error")); + return v4->currentContext()->throwSyntaxError(QLatin1String("Parse error")); QQmlJS::V4IR::Module module(v4->debugger != 0); - QQmlJS::RuntimeCodegen cg(v4->current, f->strictMode); + QQmlJS::RuntimeCodegen cg(v4->currentContext(), f->strictMode); cg.generateFromFunctionExpression(QString(), function, fe, &module); QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); QV4::Function *vmf = compilationUnit->linkToEngine(v4); @@ -348,7 +351,7 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) ScopedCallData callData(scope, len); if (len) { - if (arr->protoHasArray() || arr->hasAccessorProperty) { + if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) { for (quint32 i = 0; i < len; ++i) callData->args[i] = arr->getIndexed(i); } else { @@ -401,7 +404,7 @@ DEFINE_MANAGED_VTABLE(ScriptFunction); ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope, function->name, true) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope s(scope); ScopedValue protectThis(s, this); @@ -409,7 +412,7 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) this->function = function; this->function->compilationUnit->ref(); Q_ASSERT(function); - Q_ASSERT(function->codePtr); + Q_ASSERT(function->code); // global function if (!scope) @@ -419,12 +422,10 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) needsActivation = function->needsActivation(); strictMode = function->isStrict(); - formalParameterCount = function->formals.size(); - formalParameterList = function->formals.constData(); - defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(formalParameterCount)); + formalParameterCount = function->nArguments; + varCount = function->internalClass->size - function->nArguments; - varCount = function->locals.size(); - varList = function->locals.constData(); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(formalParameterCount)); if (scope->strictMode) { Property pd = Property::fromAccessor(v4->thrower, v4->thrower); @@ -443,13 +444,10 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) Scope scope(v4); Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that)); - InternalClass *ic = v4->objectClass; - ScopedObject proto(scope, f->memberData[Index_Prototype].value); - if (proto) - ic = v4->emptyClass->changePrototype(proto.getPointer()); + InternalClass *ic = f->internalClassForConstructor(); ScopedObject obj(scope, v4->newObject(ic)); - ExecutionContext *context = v4->current; + ExecutionContext *context = v4->currentContext(); callData->thisObject = obj.asReturnedValue(); ExecutionContext *ctx = context->newCallContext(f.getPointer(), callData); @@ -471,7 +469,7 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) return Encode::undefined(); CHECK_STACK_LIMITS(v4); - ExecutionContext *context = v4->current; + ExecutionContext *context = v4->currentContext(); Scope scope(context); CallContext *ctx = context->newCallContext(f, callData); @@ -488,7 +486,7 @@ DEFINE_MANAGED_VTABLE(SimpleScriptFunction); SimpleScriptFunction::SimpleScriptFunction(ExecutionContext *scope, Function *function) : FunctionObject(scope, function->name, true) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope s(scope); ScopedValue protectThis(s, this); @@ -496,7 +494,7 @@ SimpleScriptFunction::SimpleScriptFunction(ExecutionContext *scope, Function *fu this->function = function; this->function->compilationUnit->ref(); Q_ASSERT(function); - Q_ASSERT(function->codePtr); + Q_ASSERT(function->code); // global function if (!scope) @@ -506,12 +504,10 @@ SimpleScriptFunction::SimpleScriptFunction(ExecutionContext *scope, Function *fu needsActivation = function->needsActivation(); strictMode = function->isStrict(); - formalParameterCount = function->formals.size(); - formalParameterList = function->formals.constData(); - defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(formalParameterCount)); + formalParameterCount = function->nArguments; + varCount = function->internalClass->size - function->nArguments; - varCount = function->locals.size(); - varList = function->locals.constData(); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(formalParameterCount)); if (scope->strictMode) { Property pd = Property::fromAccessor(v4->thrower, v4->thrower); @@ -530,33 +526,29 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) Scope scope(v4); Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that)); - InternalClass *ic = v4->objectClass; - Scoped<Object> proto(scope, f->memberData[Index_Prototype].value); - if (!!proto) - ic = v4->emptyClass->changePrototype(proto.getPointer()); + InternalClass *ic = f->internalClassForConstructor(); callData->thisObject = v4->newObject(ic); - ExecutionContext *context = v4->current; + ExecutionContext *context = v4->currentContext(); + ExecutionContextSaver ctxSaver(context); - CallContext ctx; - ctx.initSimpleCallContext(v4, context); + CallContext ctx(v4); ctx.strictMode = f->strictMode; ctx.callData = callData; ctx.function = f.getPointer(); ctx.compilationUnit = f->function->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; ctx.outer = f->scope; - ctx.locals = v4->stackPush(f->function->locals.size()); + ctx.locals = v4->stackPush(f->varCount); while (callData->argc < (int)f->formalParameterCount) { callData->args[callData->argc] = Encode::undefined(); ++callData->argc; } - v4->pushContext(&ctx); + Q_ASSERT(v4->currentContext() == &ctx); if (f->function->compiledFunction->hasQmlDependencies()) QmlContextWrapper::registerQmlDependencies(v4, f->function->compiledFunction); - ExecutionContextSaver ctxSaver(context); Scoped<Object> result(scope, f->function->code(&ctx, f->function->codeData)); if (!result) @@ -574,27 +566,26 @@ ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData) SimpleScriptFunction *f = static_cast<SimpleScriptFunction *>(that); Scope scope(v4); - ExecutionContext *context = v4->current; + ExecutionContext *context = v4->currentContext(); + ExecutionContextSaver ctxSaver(context); - CallContext ctx; - ctx.initSimpleCallContext(v4, context); + CallContext ctx(v4); ctx.strictMode = f->strictMode; ctx.callData = callData; ctx.function = f; ctx.compilationUnit = f->function->compilationUnit; ctx.lookups = ctx.compilationUnit->runtimeLookups; ctx.outer = f->scope; - ctx.locals = v4->stackPush(f->function->locals.size()); + ctx.locals = v4->stackPush(f->varCount); while (callData->argc < (int)f->formalParameterCount) { callData->args[callData->argc] = Encode::undefined(); ++callData->argc; } - v4->current = &ctx; + Q_ASSERT(v4->currentContext() == &ctx); if (f->function->compiledFunction->hasQmlDependencies()) QmlContextWrapper::registerQmlDependencies(v4, f->function->compiledFunction); - ExecutionContextSaver ctxSaver(context); return f->function->code(&ctx, f->function->codeData); } @@ -607,12 +598,12 @@ BuiltinFunction::BuiltinFunction(ExecutionContext *scope, const StringRef name, : FunctionObject(scope, name) , code(code) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue BuiltinFunction::construct(Managed *f, CallData *) { - return f->internalClass->engine->current->throwTypeError(); + return f->internalClass->engine->currentContext()->throwTypeError(); } ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) @@ -623,15 +614,14 @@ ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) return Encode::undefined(); CHECK_STACK_LIMITS(v4); - ExecutionContext *context = v4->current; + ExecutionContext *context = v4->currentContext(); + ExecutionContextSaver ctxSaver(context); - CallContext ctx; - ctx.initSimpleCallContext(v4, context); + CallContext ctx(v4); ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? ctx.callData = callData; - v4->pushContext(&ctx); + Q_ASSERT(v4->currentContext() == &ctx); - ExecutionContextSaver ctxSaver(context); return f->code(&ctx); } @@ -643,16 +633,14 @@ ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData) return Encode::undefined(); CHECK_STACK_LIMITS(v4); - ExecutionContext *context = v4->current; - Scope scope(v4); + ExecutionContext *context = v4->currentContext(); + ExecutionContextSaver ctxSaver(context); - CallContext ctx; - ctx.initSimpleCallContext(v4, context); + CallContext ctx(v4); ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? ctx.callData = callData; - v4->pushContext(&ctx); + Q_ASSERT(v4->currentContext() == &ctx); - ExecutionContextSaver ctxSaver(context); return f->code(&ctx, f->index); } @@ -665,7 +653,8 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObjectRef target, , target(target) , boundArgs(boundArgs) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); + subtype = FunctionObject::BoundFunction; this->boundThis = boundThis; Scope s(scope); @@ -717,12 +706,6 @@ ReturnedValue BoundFunction::construct(Managed *that, CallData *dd) return f->target->construct(callData); } -bool BoundFunction::hasInstance(Managed *that, const ValueRef value) -{ - BoundFunction *f = static_cast<BoundFunction *>(that); - return FunctionObject::hasInstance(f->target, value); -} - void BoundFunction::markObjects(Managed *that, ExecutionEngine *e) { BoundFunction *o = static_cast<BoundFunction *>(that); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 2fc36d862f..96534cb68c 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -97,7 +97,8 @@ struct Q_QML_EXPORT FunctionObject: Object { // Used with Managed::subType enum FunctionType { RegularFunction = 0, - WrappedQtMethod = 1 + WrappedQtMethod = 1, + BoundFunction }; enum { @@ -107,11 +108,13 @@ struct Q_QML_EXPORT FunctionObject: Object { ExecutionContext *scope; SafeString name; - String * const *formalParameterList; - String * const *varList; unsigned int formalParameterCount; unsigned int varCount; Function *function; + InternalClass *protoCacheClass; + uint protoCacheIndex; + ReturnedValue protoValue; + InternalClass *classForConstructor; FunctionObject(ExecutionContext *scope, const StringRef name, bool createProto = false); FunctionObject(ExecutionContext *scope, const QString &name = QString(), bool createProto = false); @@ -124,10 +127,10 @@ struct Q_QML_EXPORT FunctionObject: Object { static ReturnedValue construct(Managed *that, CallData *); static ReturnedValue call(Managed *that, CallData *d); inline ReturnedValue construct(CallData *callData) { - return vtbl->construct(this, callData); + return internalClass->vtable->construct(this, callData); } inline ReturnedValue call(CallData *callData) { - return vtbl->call(this, callData); + return internalClass->vtable->call(this, callData); } static FunctionObject *cast(const Value &v) { @@ -136,11 +139,13 @@ struct Q_QML_EXPORT FunctionObject: Object { static FunctionObject *creatScriptFunction(ExecutionContext *scope, Function *function); + ReturnedValue protoProperty(); + InternalClass *internalClassForConstructor(); + protected: FunctionObject(InternalClass *ic); static void markObjects(Managed *that, ExecutionEngine *e); - static bool hasInstance(Managed *that, const ValueRef value); static void destroy(Managed *that) { static_cast<FunctionObject*>(that)->~FunctionObject(); } }; @@ -192,12 +197,12 @@ struct IndexedBuiltinFunction: FunctionObject , code(code) , index(index) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } static ReturnedValue construct(Managed *m, CallData *) { - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); } static ReturnedValue call(Managed *that, CallData *callData); @@ -235,7 +240,6 @@ struct BoundFunction: FunctionObject { static void destroy(Managed *); static void markObjects(Managed *that, ExecutionEngine *e); - static bool hasInstance(Managed *that, const ValueRef value); }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 7d7338f19c..1d465df0c0 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -119,6 +119,8 @@ struct Object; struct ObjectPrototype; struct ObjectIterator; struct ExecutionContext; +struct GlobalContext; +struct CallContext; struct ScriptFunction; struct InternalClass; struct Property; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 82622de5bb..fa8af8ed5d 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -349,7 +349,7 @@ DEFINE_MANAGED_VTABLE(EvalFunction); EvalFunction::EvalFunction(ExecutionContext *scope) : FunctionObject(scope, scope->engine->id_eval) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(1)); } @@ -379,15 +379,17 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) if (callData->argc < 1) return Encode::undefined(); - ExecutionContext *parentContext = engine()->current; - ExecutionEngine *engine = parentContext->engine; + ExecutionEngine *v4 = engine(); + ExecutionContext *parentContext = v4->currentContext(); + ExecutionContextSaver ctxSaver(parentContext); + ExecutionContext *ctx = parentContext; Scope scope(ctx); if (!directCall) { // the context for eval should be the global scope, so we fake a root // context - ctx = engine->pushGlobalContext(); + ctx = v4->pushGlobalContext(); } if (!callData->args[0].isString()) @@ -418,7 +420,6 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) return e->call(callData); } - ExecutionContextSaver ctxSaver(parentContext); ContextStateSaver stateSaver(ctx); ExecutionContext::EvalCode evalCode; @@ -437,7 +438,6 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) ReturnedValue EvalFunction::call(Managed *that, CallData *callData) { // indirect call - // ### const_cast return static_cast<EvalFunction *>(that)->evalCall(callData, false); } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 8f2548064a..d0e0e9413b 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -108,7 +108,7 @@ void QV4Include::callback(const QV4::ValueRef callback, const QV4::ValueRef stat if (!f) return; - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::ScopedCallData callData(scope, 1); callData->thisObject = v4->globalObject->asReturnedValue(); callData->args[0] = status; @@ -153,7 +153,7 @@ void QV4Include::finished() QV4::ScopedObject qmlglobal(scope, m_qmlglobal.value()); QV4::Script script(v4, qmlglobal, code, m_url.toString()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::ScopedString status(scope, v4->newString(QStringLiteral("status"))); script.parse(); if (!scope.engine->hasException) @@ -220,7 +220,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QV4::Script script(v4, qmlcontextobject, code, url.toString()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); script.parse(); if (!v4->hasException) script.run(); diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index cb799a473c..29ede3d104 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -126,10 +126,21 @@ uint PropertyHash::lookup(const Identifier *identifier) const } } +InternalClass::InternalClass(ExecutionEngine *engine) + : engine(engine) + , prototype(0) + , vtable(&Managed::static_vtbl) + , m_sealed(0) + , m_frozen(0) + , size(0) +{ +} + InternalClass::InternalClass(const QV4::InternalClass &other) : engine(other.engine) , prototype(other.prototype) + , vtable(other.vtable) , propertyTable(other.propertyTable) , nameMap(other.nameMap) , propertyData(other.propertyData) @@ -169,6 +180,12 @@ InternalClass *InternalClass::changeMember(String *string, PropertyAttributes da } +InternalClass *InternalClass::create(ExecutionEngine *engine, const ManagedVTable *vtable, Object *proto) +{ + InternalClass *c = engine->emptyClass->changeVTable(vtable); + return c->changePrototype(proto); +} + InternalClass *InternalClass::changePrototype(Object *proto) { if (prototype == proto) @@ -184,11 +201,41 @@ InternalClass *InternalClass::changePrototype(Object *proto) // create a new class and add it to the tree InternalClass *newClass; - if (this == engine->emptyClass) { + if (!size) { newClass = engine->newClass(*this); newClass->prototype = proto; } else { - newClass = engine->emptyClass->changePrototype(proto); + newClass = engine->emptyClass->changeVTable(vtable); + newClass = newClass->changePrototype(proto); + for (uint i = 0; i < size; ++i) + newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + } + + transitions.insert(t, newClass); + return newClass; +} + +InternalClass *InternalClass::changeVTable(const ManagedVTable *vt) +{ + if (vtable == vt) + return this; + + Transition t; + t.vtable = vt; + t.flags = Transition::VTableChange; + + QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t); + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass; + if (this == engine->emptyClass) { + newClass = engine->newClass(*this); + newClass->vtable = vt; + } else { + newClass = engine->emptyClass->changeVTable(vt); + newClass = newClass->changePrototype(prototype); for (uint i = 0; i < size; ++i) newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); } @@ -250,7 +297,8 @@ void InternalClass::removeMember(Object *object, Identifier *id) } // create a new class and add it to the tree - object->internalClass = engine->emptyClass->changePrototype(prototype); + object->internalClass = engine->emptyClass->changeVTable(vtable); + object->internalClass = object->internalClass->changePrototype(prototype); for (uint i = 0; i < size; ++i) { if (i == propIdx) continue; @@ -283,6 +331,7 @@ InternalClass *InternalClass::sealed() return m_sealed; m_sealed = engine->emptyClass; + m_sealed = m_sealed->changeVTable(vtable); m_sealed = m_sealed->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); @@ -300,6 +349,7 @@ InternalClass *InternalClass::frozen() return m_frozen; m_frozen = engine->emptyClass; + m_frozen = m_frozen->changeVTable(vtable); m_frozen = m_frozen->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); @@ -343,7 +393,9 @@ void InternalClass::markObjects() for (QHash<Transition, InternalClass *>::ConstIterator it = transitions.begin(), end = transitions.end(); it != end; ++it) { - if (it.key().flags == Transition::ProtoChange) { + if (it.key().flags == Transition::VTableChange) { + it.value()->markObjects(); + } else if (it.key().flags == Transition::ProtoChange) { Q_ASSERT(it.value()->prototype); it.value()->prototype->mark(engine); } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 9586637b32..b25b895183 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -53,6 +53,7 @@ struct String; struct ExecutionEngine; struct Object; struct Identifier; +struct ManagedVTable; struct PropertyHashData; struct PropertyHash @@ -198,9 +199,14 @@ struct InternalClassTransition union { Identifier *id; Object *prototype; + const ManagedVTable *vtable; }; int flags; - enum { ProtoChange = 0x100 }; + enum { + // range 0-0xff is reserved for attribute changes + ProtoChange = 0x100, + VTableChange = 0x200 + }; bool operator==(const InternalClassTransition &other) const { return id == other.id && flags == other.flags; } @@ -210,6 +216,8 @@ uint qHash(const QV4::InternalClassTransition &t, uint = 0); struct InternalClass { ExecutionEngine *engine; Object *prototype; + const ManagedVTable *vtable; + PropertyHash propertyTable; // id to valueIndex SharedInternalClassData<String *> nameMap; SharedInternalClassData<PropertyAttributes> propertyData; @@ -222,7 +230,9 @@ struct InternalClass { uint size; + static InternalClass *create(ExecutionEngine *engine, const ManagedVTable *vtable, Object *proto); InternalClass *changePrototype(Object *proto); + InternalClass *changeVTable(const ManagedVTable *vt); InternalClass *addMember(StringRef string, PropertyAttributes data, uint *index = 0); InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); @@ -238,7 +248,7 @@ struct InternalClass { private: friend struct ExecutionEngine; - InternalClass(ExecutionEngine *engine) : engine(engine), prototype(0), m_sealed(0), m_frozen(0), size(0) {} + InternalClass(ExecutionEngine *engine); InternalClass(const InternalClass &other); }; diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 5aac8c8197..6633435668 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -965,7 +965,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) ReturnedValue JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value) { if (value.isString()) - return engine->current->engine->newString(value.toString())->asReturnedValue(); + return engine->currentContext()->engine->newString(value.toString())->asReturnedValue(); else if (value.isDouble()) return Encode(value.toDouble()); else if (value.isBool()) diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index f63e7726f5..3bcbdeadbf 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -38,8 +38,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QV4JSONOBJECTS_H -#define QV4SJONOBJECTS_H +#ifndef QV4JSONOBJECT_H +#define QV4JSONOBJECT_H #include "qv4object_p.h" #include <qjsonarray.h> diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index f67b24c040..a870cdac61 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -87,13 +87,13 @@ ReturnedValue Lookup::getterGeneric(QV4::Lookup *l, const ValueRef object) switch (object->type()) { case Value::Undefined_Type: case Value::Null_Type: - return engine->current->throwTypeError(); + return engine->currentContext()->throwTypeError(); case Value::Boolean_Type: proto = engine->booleanClass->prototype; break; case Value::Managed_Type: Q_ASSERT(object->isString()); - proto = engine->stringClass->prototype; + proto = engine->stringObjectClass->prototype; if (l->name->equals(engine->id_length)) { // special case, as the property is on the object itself l->getter = stringLengthGetter; @@ -446,7 +446,7 @@ void Lookup::setterGeneric(Lookup *l, const ValueRef object, const ValueRef valu Scope scope(l->name->engine()); ScopedObject o(scope, object); if (!o) { - o = __qmljs_convert_to_object(scope.engine->current, object); + o = __qmljs_convert_to_object(scope.engine->currentContext(), object); if (!o) // type error return; ScopedString s(scope, l->name); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 6455a08037..fef7489110 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -52,7 +52,6 @@ const ManagedVTable Managed::static_vtbl = 0 /*markObjects*/, destroy, 0 /*collectDeletables*/, - hasInstance, 0, 0, 0, @@ -82,7 +81,6 @@ void Managed::operator delete(void *ptr) return; Managed *m = static_cast<Managed *>(ptr); - m->vtbl = 0; m->_data = 0; m->markBit = 0; m->~Managed(); @@ -178,29 +176,30 @@ QString Managed::className() const return QString::fromLatin1(s); } -bool Managed::hasInstance(Managed *m, const ValueRef) +void Managed::setVTable(const ManagedVTable *vt) { - return m->engine()->current->throwTypeError(); + Q_ASSERT(internalClass); + internalClass = internalClass->changeVTable(vt); } ReturnedValue Managed::construct(Managed *m, CallData *) { - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); } ReturnedValue Managed::call(Managed *m, CallData *) { - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); } ReturnedValue Managed::getLookup(Managed *m, Lookup *) { - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); } void Managed::setLookup(Managed *m, Lookup *, const ValueRef) { - m->engine()->current->throwTypeError(); + m->engine()->currentContext()->throwTypeError(); } bool Managed::isEqualTo(Managed *, Managed *) @@ -208,47 +207,42 @@ bool Managed::isEqualTo(Managed *, Managed *) return false; } -bool Managed::hasInstance(const ValueRef v) -{ - return vtbl->hasInstance(this, v); -} - ReturnedValue Managed::get(const StringRef name, bool *hasProperty) { - return vtbl->get(this, name, hasProperty); + return internalClass->vtable->get(this, name, hasProperty); } ReturnedValue Managed::getIndexed(uint index, bool *hasProperty) { - return vtbl->getIndexed(this, index, hasProperty); + return internalClass->vtable->getIndexed(this, index, hasProperty); } void Managed::put(const StringRef name, const ValueRef value) { - vtbl->put(this, name, value); + internalClass->vtable->put(this, name, value); } void Managed::setLookup(Lookup *l, const ValueRef v) { - vtbl->setLookup(this, l, v); + internalClass->vtable->setLookup(this, l, v); } void Managed::putIndexed(uint index, const ValueRef value) { - vtbl->putIndexed(this, index, value); + internalClass->vtable->putIndexed(this, index, value); } PropertyAttributes Managed::query(StringRef name) const { - return vtbl->query(this, name); + return internalClass->vtable->query(this, name); } bool Managed::deleteProperty(const StringRef name) { - return vtbl->deleteProperty(this, name); + return internalClass->vtable->deleteProperty(this, name); } Property *Managed::advanceIterator(ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes) { - return vtbl->advanceIterator(this, it, name, index, attributes); + return internalClass->vtable->advanceIterator(this, it, name, index, attributes); } diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 47ac5e05e4..63972688a7 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -46,6 +46,7 @@ #include <QtCore/QDebug> #include "qv4global_p.h" #include "qv4value_def_p.h" +#include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -84,7 +85,6 @@ struct ManagedVTable void (*markObjects)(Managed *, ExecutionEngine *e); void (*destroy)(Managed *); void (*collectDeletables)(Managed *, GCDeletable **deletable); - bool (*hasInstance)(Managed *, const ValueRef value); ReturnedValue (*get)(Managed *, const StringRef name, bool *hasProperty); ReturnedValue (*getIndexed)(Managed *, uint index, bool *hasProperty); void (*put)(Managed *, const StringRef name, const ValueRef value); @@ -108,7 +108,6 @@ const QV4::ManagedVTable classname::static_vtbl = \ markObjects, \ destroy, \ 0, \ - hasInstance, \ get, \ getIndexed, \ put, \ @@ -132,7 +131,6 @@ const QV4::ManagedVTable classname::static_vtbl = \ markObjects, \ destroy, \ collectDeletables, \ - hasInstance, \ get, \ getIndexed, \ put, \ @@ -158,11 +156,15 @@ private: protected: Managed(InternalClass *internal) - : _data(0), vtbl(&static_vtbl), internalClass(internal) - { inUse = 1; extensible = 1; } + : internalClass(internal), _data(0) + { + Q_ASSERT(!internalClass || internalClass->vtable); + inUse = 1; extensible = 1; + } public: void *operator new(size_t size, MemoryManager *mm); + void *operator new(size_t, Managed *m) { return m; } void operator delete(void *ptr); void operator delete(void *ptr, MemoryManager *mm); @@ -194,12 +196,12 @@ public: template <typename T> T *as() { // ### FIXME: - if (!this) + if (!this || !internalClass) return 0; #if !defined(QT_NO_QOBJECT_CHECK) reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(this)); #endif - return vtbl == &T::static_vtbl ? static_cast<T *>(this) : 0; + return internalClass->vtable == &T::static_vtbl ? static_cast<T *>(this) : 0; } template <typename T> const T *as() const { @@ -209,7 +211,7 @@ public: #if !defined(QT_NO_QOBJECT_CHECK) reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(const_cast<Managed *>(this))); #endif - return vtbl == &T::static_vtbl ? static_cast<const T *>(this) : 0; + return internalClass->vtable == &T::static_vtbl ? static_cast<const T *>(this) : 0; } String *asString() { return type == Type_String ? reinterpret_cast<String *>(this) : 0; } @@ -240,7 +242,8 @@ public: *reinterpret_cast<Managed **>(this) = m; } - bool hasInstance(const ValueRef v); + void setVTable(const ManagedVTable *vt); + ReturnedValue construct(CallData *d); ReturnedValue call(CallData *d); ReturnedValue get(const StringRef name, bool *hasProperty = 0); @@ -249,21 +252,20 @@ public: void putIndexed(uint index, const ValueRef value); PropertyAttributes query(StringRef name) const; PropertyAttributes queryIndexed(uint index) const - { return vtbl->queryIndexed(this, index); } + { return internalClass->vtable->queryIndexed(this, index); } bool deleteProperty(const StringRef name); bool deleteIndexedProperty(uint index) - { return vtbl->deleteIndexedProperty(this, index); } + { return internalClass->vtable->deleteIndexedProperty(this, index); } ReturnedValue getLookup(Lookup *l) - { return vtbl->getLookup(this, l); } + { return internalClass->vtable->getLookup(this, l); } void setLookup(Lookup *l, const ValueRef v); bool isEqualTo(Managed *other) - { return vtbl->isEqualTo(this, other); } + { return internalClass->vtable->isEqualTo(this, other); } Property *advanceIterator(ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes); static void destroy(Managed *that) { that->_data = 0; } - static bool hasInstance(Managed *that, const ValueRef value); static ReturnedValue construct(Managed *m, CallData *d); static ReturnedValue call(Managed *m, CallData *); static ReturnedValue getLookup(Managed *m, Lookup *); @@ -276,6 +278,9 @@ public: ReturnedValue asReturnedValue() { return Value::fromManaged(this).asReturnedValue(); } + + InternalClass *internalClass; + enum { SimpleArray = 1 }; @@ -297,11 +302,6 @@ public: }; }; -protected: - const ManagedVTable *vtbl; -public: - InternalClass *internalClass; - private: friend class MemoryManager; friend struct Identifiers; @@ -337,10 +337,10 @@ inline FunctionObject *managed_cast(Managed *m) inline ReturnedValue Managed::construct(CallData *d) { - return vtbl->construct(this, d); + return internalClass->vtable->construct(this, d); } inline ReturnedValue Managed::call(CallData *d) { - return vtbl->call(this, d); + return internalClass->vtable->call(this, d); } } diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index dca75e6c28..6fe3db3950 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -39,7 +39,7 @@ ** ****************************************************************************/ #ifndef QV4MATHOBJECT_H -#define QV$MATHOBJECT_H +#define QV4MATHOBJECT_H #include "qv4object_p.h" diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index 9eb3ae7ec9..f67efaffb9 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -155,6 +155,19 @@ struct MemoryManager::Data QVector<Chunk> heapChunks; + + struct LargeItem { + LargeItem *next; + void *data; + + Managed *managed() { + return reinterpret_cast<Managed *>(&data); + } + }; + + LargeItem *largeItems; + + // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; @@ -167,6 +180,7 @@ struct MemoryManager::Data , stackTop(0) , totalItems(0) , totalAlloc(0) + , largeItems(0) { memset(smallItems, 0, sizeof(smallItems)); memset(nChunks, 0, sizeof(nChunks)); @@ -200,7 +214,6 @@ bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::C MemoryManager::MemoryManager() : m_d(new Data(true)) - , m_contextList(0) , m_persistentValues(0) , m_weakValues(0) { @@ -258,8 +271,14 @@ Managed *MemoryManager::alloc(std::size_t size) size_t pos = size >> 4; - // fits into a small bucket - Q_ASSERT(size < MemoryManager::Data::MaxItemSize); + // doesn't fit into a small bucket + if (size >= MemoryManager::Data::MaxItemSize) { + // we use malloc for this + MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(malloc(size + sizeof(MemoryManager::Data::LargeItem))); + item->next = m_d->largeItems; + m_d->largeItems = item; + return item->managed(); + } Managed *m = m_d->smallItems[pos]; if (m) @@ -279,11 +298,11 @@ Managed *MemoryManager::alloc(std::size_t size) uint shift = ++m_d->nChunks[pos]; if (shift > 10) shift = 10; - std::size_t allocSize = CHUNK_SIZE*(1 << shift); + std::size_t allocSize = CHUNK_SIZE*(size_t(1) << shift); allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); Data::Chunk allocation; allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); - allocation.chunkSize = size; + allocation.chunkSize = int(size); m_d->heapChunks.append(allocation); std::sort(m_d->heapChunks.begin(), m_d->heapChunks.end()); char *chunk = (char *)allocation.memory.base(); @@ -301,8 +320,9 @@ Managed *MemoryManager::alloc(std::size_t size) } *last = 0; m = m_d->smallItems[pos]; - m_d->availableItems[pos] += allocation.memory.size()/size - 1; - m_d->totalItems += allocation.memory.size()/size - 1; + const size_t increase = allocation.memory.size()/size - 1; + m_d->availableItems[pos] += uint(increase); + m_d->totalItems += int(increase); #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize); #endif @@ -404,8 +424,8 @@ void MemoryManager::mark() // now that we marked all roots, start marking recursively and popping from the mark stack while (m_d->engine->jsStackTop > markBase) { Managed *m = m_d->engine->popForGC(); - Q_ASSERT (m->vtbl->markObjects); - m->vtbl->markObjects(m, m_d->engine); + Q_ASSERT (m->internalClass->vtable->markObjects); + m->internalClass->vtable->markObjects(m, m_d->engine); } } @@ -447,18 +467,21 @@ void MemoryManager::sweep(bool lastSweep) for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize, &deletable); - ExecutionContext *ctx = m_contextList; - ExecutionContext **n = &m_contextList; - while (ctx) { - ExecutionContext *next = ctx->next; - if (!ctx->marked) { - free(ctx); - *n = next; - } else { - ctx->marked = false; - n = &ctx->next; + Data::LargeItem *i = m_d->largeItems; + Data::LargeItem **last = &m_d->largeItems; + while (i) { + Managed *m = i->managed(); + Q_ASSERT(m->inUse); + if (m->markBit) { + m->markBit = 0; + last = &i->next; + i = i->next; + continue; } - ctx = next; + + *last = i->next; + free(i); + i = *last; } deletable = *firstDeletable; @@ -493,9 +516,9 @@ void MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, #ifdef V4_USE_VALGRIND VALGRIND_ENABLE_ERROR_REPORTING; #endif - if (m->vtbl->collectDeletables) - m->vtbl->collectDeletables(m, deletable); - m->vtbl->destroy(m); + if (m->internalClass->vtable->collectDeletables) + m->internalClass->vtable->collectDeletables(m, deletable); + m->internalClass->vtable->destroy(m); m->setNextFree(*f); #ifdef V4_USE_VALGRIND diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h index f3258519de..7d28319536 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/jsruntime/qv4mm_p.h @@ -103,8 +103,6 @@ public: return o; } - ExecutionContext *allocContext(uint size); - bool isGCBlocked() const; void setGCBlocked(bool blockGC); void runGC(); @@ -134,21 +132,11 @@ private: protected: QScopedPointer<Data> m_d; - ExecutionContext *m_contextList; public: PersistentValuePrivate *m_persistentValues; PersistentValuePrivate *m_weakValues; }; -inline ExecutionContext *MemoryManager::allocContext(uint size) -{ - ExecutionContext *newContext = (ExecutionContext *)malloc(size); - newContext->next = m_contextList; - m_contextList = newContext; - return newContext; -} - - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 039b790aed..a363a06242 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -54,7 +54,7 @@ DEFINE_MANAGED_VTABLE(NumberObject); NumberCtor::NumberCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Number")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue NumberCtor::construct(Managed *m, CallData *callData) diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index cca7d2b26a..106525d412 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -74,18 +74,17 @@ Object::Object(ExecutionEngine *engine) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) { - vtbl = &static_vtbl; type = Type_Object; flags = SimpleArray; memset(memberData, 0, sizeof(Property)*memberDataAlloc); } -Object::Object(InternalClass *internalClass) - : Managed(internalClass) +Object::Object(InternalClass *ic) + : Managed(ic) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) { - vtbl = &static_vtbl; + Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl); type = Type_Object; flags = SimpleArray; @@ -169,8 +168,8 @@ void Object::putValue(Property *pd, PropertyAttributes attrs, const ValueRef val return; reject: - if (engine()->current->strictMode) - engine()->current->throwTypeError(); + if (engine()->currentContext()->strictMode) + engine()->currentContext()->throwTypeError(); } void Object::defineDefaultProperty(const StringRef name, ValueRef value) @@ -721,7 +720,7 @@ void Object::internalPut(const StringRef name, const ValueRef value) bool ok; uint l = value->asArrayLength(&ok); if (!ok) { - engine()->current->throwRangeError(value); + engine()->currentContext()->throwRangeError(value); return; } ok = setArrayLength(l); @@ -769,11 +768,11 @@ void Object::internalPut(const StringRef name, const ValueRef value) } reject: - if (engine()->current->strictMode) { + if (engine()->currentContext()->strictMode) { QString message = QStringLiteral("Cannot assign to read-only property \""); message += name->toQString(); message += QLatin1Char('\"'); - engine()->current->throwTypeError(message); + engine()->currentContext()->throwTypeError(message); } } @@ -844,8 +843,8 @@ void Object::internalPutIndexed(uint index, const ValueRef value) return; reject: - if (engine()->current->strictMode) - engine()->current->throwTypeError(); + if (engine()->currentContext()->strictMode) + engine()->currentContext()->throwTypeError(); } // Section 8.12.7 @@ -867,8 +866,8 @@ bool Object::internalDeleteProperty(const StringRef name) memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); return true; } - if (engine()->current->strictMode) - engine()->current->throwTypeError(); + if (engine()->currentContext()->strictMode) + engine()->currentContext()->throwTypeError(); return false; } @@ -897,8 +896,8 @@ bool Object::internalDeleteIndexedProperty(uint index) return true; } - if (engine()->current->strictMode) - engine()->current->throwTypeError(); + if (engine()->currentContext()->strictMode) + engine()->currentContext()->throwTypeError(); return false; } @@ -1133,7 +1132,7 @@ ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endInd Scope scope(engine()); ScopedValue value(scope); - if (!(flags & SimpleArray) || o->protoHasArray() || o->arrayAttributes) { + if (!(o->flags & SimpleArray) || o->protoHasArray()) { // lets be safe and slow for (uint i = fromIndex; i < endIndex; ++i) { bool exists; @@ -1262,13 +1261,9 @@ void Object::arraySort(ExecutionContext *context, ObjectRef thisObject, const Va ArrayElementLessThan lessThan(context, thisObject, comparefn); + if (!len) + return; Property *begin = arrayData; - // We deliberately choose qSort over std::sort here, because with - // MSVC in debug builds, std::sort has an ASSERT() that verifies - // that the return values of lessThan are perfectly consistent - // and aborts otherwise. We do not want JavaScript to easily crash - // the entire application and therefore choose qSort, which doesn't - // have this property. std::sort(begin, begin + len, lessThan); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index daef18d4e2..23f2f682fd 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -280,13 +280,13 @@ public: void ensureMemberIndex(uint idx); inline ReturnedValue get(const StringRef name, bool *hasProperty = 0) - { return vtbl->get(this, name, hasProperty); } + { return internalClass->vtable->get(this, name, hasProperty); } inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) - { return vtbl->getIndexed(this, idx, hasProperty); } + { return internalClass->vtable->getIndexed(this, idx, hasProperty); } inline void put(const StringRef name, const ValueRef v) - { vtbl->put(this, name, v); } + { internalClass->vtable->put(this, name, v); } inline void putIndexed(uint idx, const ValueRef v) - { vtbl->putIndexed(this, idx, v); } + { internalClass->vtable->putIndexed(this, idx, v); } using Managed::get; using Managed::getIndexed; using Managed::put; @@ -331,14 +331,13 @@ struct BooleanObject: Object { SafeValue value; BooleanObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->booleanClass) { - vtbl = &static_vtbl; type = Type_BooleanObject; value = val; } protected: BooleanObject(InternalClass *ic) : Object(ic) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_BooleanObject; value = Encode(false); } @@ -349,14 +348,13 @@ struct NumberObject: Object { SafeValue value; NumberObject(ExecutionEngine *engine, const ValueRef val) : Object(engine->numberClass) { - vtbl = &static_vtbl; type = Type_NumberObject; value = val; } protected: NumberObject(InternalClass *ic) : Object(ic) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_NumberObject; value = Encode((int)0); } diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 62595b5176..04fa504991 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -42,6 +42,7 @@ #include "qv4object_p.h" #include "qv4stringobject_p.h" #include "qv4identifier_p.h" +#include "qv4argumentsobject_p.h" using namespace QV4; @@ -56,6 +57,11 @@ ObjectIterator::ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const object = o; current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); + + if (object && object->isNonStrictArgumentsObject) { + Scope scope(object->engine()); + Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); + } } ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) @@ -69,6 +75,11 @@ ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) object = o; current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); + + if (object && object->isNonStrictArgumentsObject) { + Scope scope(object->engine()); + Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); + } } Property *ObjectIterator::next(StringRef name, uint *index, PropertyAttributes *attrs) diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 19aedf3766..6c333b328c 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -89,7 +89,7 @@ struct ForEachIteratorObject: Object { ObjectIterator it; ForEachIteratorObject(ExecutionContext *ctx, const ObjectRef o) : Object(ctx->engine), it(workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_ForeachIteratorObject; } diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 4369267278..7ca790b970 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -41,6 +41,7 @@ #include "qv4objectproto_p.h" +#include "qv4argumentsobject_p.h" #include "qv4mm_p.h" #include "qv4scopedvalue_p.h" #include <QtCore/qnumeric.h> @@ -76,7 +77,7 @@ DEFINE_MANAGED_VTABLE(ObjectCtor); ObjectCtor::ObjectCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("Object")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) @@ -91,14 +92,14 @@ ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) obj->setPrototype(proto.getPointer()); return obj.asReturnedValue(); } - return __qmljs_to_object(v4->current, ValueRef(&callData->args[0])); + return __qmljs_to_object(v4->currentContext(), ValueRef(&callData->args[0])); } ReturnedValue ObjectCtor::call(Managed *m, CallData *callData) { if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) return m->engine()->newObject()->asReturnedValue(); - return __qmljs_to_object(m->engine()->current, ValueRef(&callData->args[0])); + return __qmljs_to_object(m->engine()->currentContext(), ValueRef(&callData->args[0])); } void ObjectPrototype::init(ExecutionEngine *v4, ObjectRef ctor) @@ -156,6 +157,9 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (!O) return ctx->throwTypeError(); + if (O->isNonStrictArgumentsObject) + Scoped<ArgumentsObject>(scope, O)->fullyCreate(); + ScopedValue v(scope, ctx->argument(1)); Scoped<String> name(scope, v->toString(ctx)); if (scope.hasException()) @@ -283,6 +287,9 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) if (!o) return ctx->throwTypeError(); + if (o->isNonStrictArgumentsObject) + Scoped<ArgumentsObject>(scope, o)->fullyCreate(); + o->extensible = false; o->internalClass = o->internalClass->frozen(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index f1b0e0bdc4..61f92a0f5c 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -241,7 +241,7 @@ QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) : Object(engine) , m_object(object) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope scope(engine); ScopedObject protectThis(scope, this); @@ -331,23 +331,23 @@ ReturnedValue QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextD if (hasProperty) *hasProperty = true; - return getProperty(ctx, result); + return getProperty(m_object, ctx, result); } -ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired) { QV4::Scope scope(ctx); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); return vmemo->vmeMethod(property->coreIndex); } else if (property->isV4Function()) { QV4::Scoped<QV4::Object> qmlcontextobject(scope, ctx->engine->qmlContextObject()); - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, property->coreIndex, qmlcontextobject); + return QV4::QObjectMethod::create(ctx->engine->rootContext, object, property->coreIndex, qmlcontextobject); } else if (property->isSignalHandler()) { - QV4::Scoped<QV4::QmlSignalHandler> handler(scope, new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, property->coreIndex)); + QV4::Scoped<QV4::QmlSignalHandler> handler(scope, new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, object, property->coreIndex)); QV4::ScopedString connect(scope, ctx->engine->newIdentifier(QStringLiteral("connect"))); QV4::ScopedString disconnect(scope, ctx->engine->newIdentifier(QStringLiteral("disconnect"))); @@ -356,7 +356,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, QQmlPropertyDat return handler.asReturnedValue(); } else { - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, property->coreIndex); + return QV4::QObjectMethod::create(ctx->engine->rootContext, object, property->coreIndex); } } @@ -369,14 +369,14 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, QQmlPropertyDat if (ep && ep->propertyCapture && property->accessors->notifier) nptr = &n; - QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, m_object, *property, nptr)); + QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, object, *property, nptr)); if (captureRequired) { if (property->accessors->notifier) { if (n) ep->captureProperty(n); } else { - ep->captureProperty(m_object, property->coreIndex, property->notifyIndex); + ep->captureProperty(object, property->coreIndex, property->notifyIndex); } } @@ -384,16 +384,16 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, QQmlPropertyDat } if (captureRequired && ep && !property->isConstant()) - ep->captureProperty(m_object, property->coreIndex, property->notifyIndex); + ep->captureProperty(object, property->coreIndex, property->notifyIndex); if (property->isVarProperty()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); return vmemo->vmeProperty(property->coreIndex); } else if (property->isDirect()) { - return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, m_object, *property, 0); + return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, object, *property, 0); } else { - return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, m_object, *property, 0); + return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, object, *property, 0); } } @@ -583,7 +583,7 @@ ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) ddata->jsEngineId == 0 || // No one owns the QObject !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted - QV4::ScopedValue rv(scope, create(engine, ddata, object)); + QV4::ScopedValue rv(scope, create(engine, object)); ddata->jsWrapper = rv; ddata->jsEngineId = engine->m_engineId; return rv.asReturnedValue(); @@ -598,14 +598,14 @@ ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) // If our tainted handle doesn't exist or has been collected, and there isn't // a handle in the ddata, we can assume ownership of the ddata->v8object if (ddata->jsWrapper.isUndefined() && !alternateWrapper) { - QV4::ScopedValue result(scope, create(engine, ddata, object)); + QV4::ScopedValue result(scope, create(engine, object)); ddata->jsWrapper = result; ddata->jsEngineId = engine->m_engineId; return result.asReturnedValue(); } if (!alternateWrapper) { - alternateWrapper = create(engine, ddata, object); + alternateWrapper = create(engine, object); if (!engine->m_multiplyWrappedQObjects) engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper.getPointer()); @@ -616,11 +616,11 @@ ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) } } -ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, int propertyIndex, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired) { - if (QQmlData::wasDeleted(m_object)) + if (QQmlData::wasDeleted(object)) return QV4::Encode::null(); - QQmlData *ddata = QQmlData::get(m_object, /*create*/false); + QQmlData *ddata = QQmlData::get(object, /*create*/false); if (!ddata) return QV4::Encode::undefined(); @@ -628,7 +628,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionContext *ctx, int propertyInd Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return getProperty(ctx, property, captureRequired); + return getProperty(object, ctx, property, captureRequired); } void QObjectWrapper::setProperty(ExecutionContext *ctx, int propertyIndex, const ValueRef value) @@ -655,14 +655,11 @@ bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) return false; } -ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object) +ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) { QQmlEngine *qmlEngine = engine->v8Engine->engine(); - if (!ddata->propertyCache && qmlEngine) { - ddata->propertyCache = QQmlEnginePrivate::get(qmlEngine)->cache(object); - if (ddata->propertyCache) ddata->propertyCache->addref(); - } - + if (qmlEngine) + QQmlData::ensurePropertyCache(qmlEngine, object); return (new (engine->memoryManager) QV4::QObjectWrapper(engine, object))->asReturnedValue(); } @@ -671,7 +668,7 @@ QV4::ReturnedValue QObjectWrapper::get(Managed *m, const StringRef name, bool *h QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ExecutionEngine *v4 = m->engine(); QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); - return that->getQmlProperty(v4->current, qmlContext, name.getPointer(), IgnoreRevision, hasProperty, /*includeImports*/ true); + return that->getQmlProperty(v4->currentContext(), qmlContext, name.getPointer(), IgnoreRevision, hasProperty, /*includeImports*/ true); } void QObjectWrapper::put(Managed *m, const StringRef name, const ValueRef value) @@ -683,10 +680,10 @@ void QObjectWrapper::put(Managed *m, const StringRef name, const ValueRef value) return; QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); - if (!setQmlProperty(v4->current, qmlContext, that->m_object, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value)) { + if (!setQmlProperty(v4->currentContext(), qmlContext, that->m_object, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value)) { QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); - v4->current->throwError(error); + v4->currentContext()->throwError(error); } } @@ -766,7 +763,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase Q_ASSERT(v4); QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, This->function.value()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::ScopedCallData callData(scope, argCount); callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); @@ -851,7 +848,7 @@ ReturnedValue QObjectWrapper::method_connect(CallContext *ctx) QPair<QObject *, int> signalInfo = extractQtSignal(ctx->callData->thisObject); QObject *signalObject = signalInfo.first; - int signalIndex = signalInfo.second; + int signalIndex = signalInfo.second; // in method range, not signal range! if (signalIndex < 0) V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); @@ -885,6 +882,11 @@ ReturnedValue QObjectWrapper::method_connect(CallContext *ctx) slot->thisObject = thisObject; slot->function = f; + if (QQmlData *ddata = QQmlData::get(signalObject)) { + if (QQmlPropertyCache *propertyCache = ddata->propertyCache) { + QQmlPropertyPrivate::flushSignal(signalObject, propertyCache->methodIndexToSignalIndex(signalIndex)); + } + } QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); return Encode::undefined(); @@ -1335,7 +1337,7 @@ static QV4::ReturnedValue CallPrecise(QObject *object, const QQmlPropertyData &d if (returnType == QMetaType::UnknownType) { QString typeName = QString::fromLatin1(unknownTypeError); QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); - return QV8Engine::getV4(engine)->current->throwError(error); + return QV8Engine::getV4(engine)->currentContext()->throwError(error); } if (data.hasArguments()) { @@ -1349,12 +1351,12 @@ static QV4::ReturnedValue CallPrecise(QObject *object, const QQmlPropertyData &d if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); - return QV8Engine::getV4(engine)->current->throwError(error); + return QV8Engine::getV4(engine)->currentContext()->throwError(error); } if (args[0] > callArgs->argc) { QString error = QLatin1String("Insufficient arguments"); - return QV8Engine::getV4(engine)->current->throwError(error); + return QV8Engine::getV4(engine)->currentContext()->throwError(error); } return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); @@ -1453,7 +1455,7 @@ static QV4::ReturnedValue CallOverloaded(QObject *object, const QQmlPropertyData candidate = RelatedMethod(object, candidate, dummy); } - return QV8Engine::getV4(engine)->current->throwError(error); + return QV8Engine::getV4(engine)->currentContext()->throwError(error); } } @@ -1724,7 +1726,7 @@ QObjectMethod::QObjectMethod(ExecutionContext *scope, QObject *object, int index , m_object(object) , m_index(index) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); subtype = WrappedQtMethod; m_qmlGlobal = qmlGlobal; } @@ -1780,7 +1782,7 @@ ReturnedValue QObjectMethod::call(Managed *m, CallData *callData) ReturnedValue QObjectMethod::callInternal(CallData *callData) { - ExecutionContext *context = engine()->current; + ExecutionContext *context = engine()->currentContext(); if (m_index == DestroyMethod) return method_destroy(context, callData->args, callData->argc); else if (m_index == ToStringMethod) @@ -1845,7 +1847,7 @@ QmlSignalHandler::QmlSignalHandler(ExecutionEngine *engine, QObject *object, int , m_object(object) , m_signalIndex(signalIndex) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } DEFINE_MANAGED_VTABLE(QmlSignalHandler); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index a73c96d098..07de1933c5 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -94,17 +94,17 @@ struct Q_QML_EXPORT QObjectWrapper : public QV4::Object using Object::get; - ReturnedValue getProperty(ExecutionContext *ctx, int propertyIndex, bool captureRequired); + static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired); void setProperty(ExecutionContext *ctx, int propertyIndex, const ValueRef value); protected: static bool isEqualTo(Managed *that, Managed *o); private: - ReturnedValue getProperty(ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired = true); + static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired = true); static void setProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, const ValueRef value); - static ReturnedValue create(ExecutionEngine *engine, QQmlData *ddata, QObject *object); + static ReturnedValue create(ExecutionEngine *engine, QObject *object); QObjectWrapper(ExecutionEngine *engine, QObject *object); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 5ec63061dc..41ff9f9741 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -92,14 +92,13 @@ RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ign } RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) - : Managed(engine->emptyClass) + : Managed(engine->regExpValueClass) , m_pattern(pattern) , m_cache(0) , m_subPatternCount(0) , m_ignoreCase(ignoreCase) , m_multiLine(multiline) { - vtbl = &static_vtbl; type = Type_RegExpObject; if (!engine) diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index a8597229c4..468fb34d76 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -142,7 +142,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re) void RegExpObject::init(ExecutionEngine *engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_RegExpObject; Scope scope(engine); @@ -237,12 +237,12 @@ DEFINE_MANAGED_VTABLE(RegExpCtor); RegExpCtor::RegExpCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("RegExp")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) { - ExecutionContext *ctx = m->engine()->current; + ExecutionContext *ctx = m->engine()->currentContext(); Scope scope(ctx); ScopedValue r(scope, callData->argument(0)); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index a8cabcb374..011607f0ba 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -53,6 +53,7 @@ #include "qv4scopedvalue_p.h" #include <private/qqmlcontextwrapper_p.h> #include "qv4qobjectwrapper_p.h" +#include <private/qv8engine_p.h> #include <QtCore/qmath.h> #include <QtCore/qnumeric.h> @@ -299,12 +300,34 @@ ReturnedValue __qmljs_delete_name(ExecutionContext *ctx, const StringRef name) QV4::ReturnedValue __qmljs_instanceof(ExecutionContext *ctx, const ValueRef left, const ValueRef right) { - Object *o = right->asObject(); - if (!o) + FunctionObject *f = right->asFunctionObject(); + if (!f) return ctx->throwTypeError(); - bool r = o->hasInstance(left); - return Encode(r); + if (f->subtype == FunctionObject::BoundFunction) + f = static_cast<BoundFunction *>(f)->target; + + Scope scope(ctx->engine); + ScopedObject v(scope, left); + if (!v) + return Encode(false); + + Scoped<Object> o(scope, f->protoProperty()); + if (!o) { + scope.engine->currentContext()->throwTypeError(); + return Encode(false); + } + + while (v) { + v = v->prototype(); + + if (! v) + break; + else if (o.getPointer() == v) + return Encode(true); + } + + return Encode(false); } QV4::ReturnedValue __qmljs_in(ExecutionContext *ctx, const ValueRef left, const ValueRef right) @@ -366,7 +389,7 @@ ReturnedValue __qmljs_object_default_value(Object *object, int typeHint) if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); - ExecutionContext *ctx = engine->current; + ExecutionContext *ctx = engine->currentContext(); Scope scope(ctx); ScopedCallData callData(scope, 0); callData->thisObject = object; @@ -966,10 +989,10 @@ ReturnedValue __qmljs_construct_value(ExecutionContext *context, const ValueRef return f->construct(callData); } -ReturnedValue __qmljs_construct_property(ExecutionContext *context, const ValueRef base, const StringRef name, CallDataRef callData) +ReturnedValue __qmljs_construct_property(ExecutionContext *context, const StringRef name, CallDataRef callData) { Scope scope(context); - ScopedObject thisObject(scope, base->toObject(context)); + ScopedObject thisObject(scope, callData->thisObject.toObject(context)); if (scope.engine->hasException) return Encode::undefined(); @@ -980,6 +1003,18 @@ ReturnedValue __qmljs_construct_property(ExecutionContext *context, const ValueR return f->construct(callData); } +ReturnedValue __qmljs_construct_property_lookup(ExecutionContext *context, uint index, CallDataRef callData) +{ + Lookup *l = context->lookups + index; + SafeValue v; + v = l->getter(l, callData->thisObject); + if (!v.isManaged()) + return context->throwTypeError(); + + return v.managed()->construct(callData); +} + + void __qmljs_throw(ExecutionContext *context, const ValueRef value) { if (!value->isEmpty()) @@ -1230,10 +1265,9 @@ ReturnedValue __qmljs_lookup_runtime_regexp(ExecutionContext *ctx, int id) return ctx->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } -ReturnedValue __qmljs_get_id_object(NoThrowContext *ctx, int id) +ReturnedValue __qmljs_get_id_array(NoThrowContext *ctx) { - QQmlContextData *context = QmlContextWrapper::callingContext(ctx->engine); - return QObjectWrapper::wrap(ctx->engine, context->idValues[id].data()); + return ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()->idObjectsArray(); } ReturnedValue __qmljs_get_context_object(NoThrowContext *ctx) @@ -1257,7 +1291,19 @@ ReturnedValue __qmljs_get_qobject_property(ExecutionContext *ctx, const ValueRef ctx->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); } - return wrapper->getProperty(ctx, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(wrapper->object(), ctx, propertyIndex, captureRequired); +} + +QV4::ReturnedValue __qmljs_get_attached_property(ExecutionContext *ctx, int attachedPropertiesId, int propertyIndex) +{ + Scope scope(ctx); + QV4::Scoped<QmlContextWrapper> c(scope, ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()); + QObject *scopeObject = c->getScopeObject(); + QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); + + QQmlEngine *qmlEngine = ctx->engine->v8Engine->engine(); + QQmlData::ensurePropertyCache(qmlEngine, attachedObject); + return QV4::QObjectWrapper::getProperty(attachedObject, ctx, propertyIndex, /*captureRequired*/true); } void __qmljs_set_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex, const ValueRef value) @@ -1277,6 +1323,11 @@ ReturnedValue __qmljs_get_imported_scripts(NoThrowContext *ctx) return context->importedScripts.value(); } +QV4::ReturnedValue __qmljs_get_qml_singleton(QV4::NoThrowContext *ctx, const QV4::StringRef name) +{ + return ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()->qmlSingletonWrapper(name); +} + void __qmljs_builtin_convert_this_to_object(ExecutionContext *ctx) { SafeValue *t = &ctx->callData->thisObject; diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index b5567693e5..9524b2459c 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -125,7 +125,8 @@ QV4::ReturnedValue __qmljs_call_element(ExecutionContext *context, const ValueRe QV4::ReturnedValue __qmljs_call_value(QV4::ExecutionContext *context, const QV4::ValueRef func, CallDataRef callData); QV4::ReturnedValue __qmljs_construct_activation_property(QV4::ExecutionContext *, const QV4::StringRef name, CallDataRef callData); -QV4::ReturnedValue __qmljs_construct_property(QV4::ExecutionContext *context, const QV4::ValueRef base, const QV4::StringRef name, CallDataRef callData); +QV4::ReturnedValue __qmljs_construct_property(QV4::ExecutionContext *context, const QV4::StringRef name, CallDataRef callData); +QV4::ReturnedValue __qmljs_construct_property_lookup(ExecutionContext *context, uint index, CallDataRef callData); QV4::ReturnedValue __qmljs_construct_value(QV4::ExecutionContext *context, const QV4::ValueRef func, CallDataRef callData); QV4::ReturnedValue __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, const QV4::ValueRef val); @@ -170,12 +171,14 @@ QV4::ReturnedValue __qmljs_construct_global_lookup(QV4::ExecutionContext *contex QV4::ReturnedValue __qmljs_get_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index); void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index, const QV4::ValueRef value); -QV4::ReturnedValue __qmljs_get_id_object(NoThrowContext *ctx, int id); +QV4::ReturnedValue __qmljs_get_id_array(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_imported_scripts(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_context_object(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_scope_object(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex, bool captureRequired); +QV4::ReturnedValue __qmljs_get_attached_property(ExecutionContext *ctx, int attachedPropertiesId, int propertyIndex); void __qmljs_set_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex, const ValueRef value); +QV4::ReturnedValue __qmljs_get_qml_singleton(NoThrowContext *ctx, const QV4::StringRef name); // For each QV4::ReturnedValue __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, const QV4::ValueRef in); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 17a19b5201..21f45745cb 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -41,7 +41,7 @@ #ifndef QV4SCOPEDVALUE_P_H #define QV4SCOPEDVALUE_P_H -#include "qv4engine_p.h" +#include "qv4context_p.h" #include "qv4value_def_p.h" QT_BEGIN_NAMESPACE @@ -231,7 +231,7 @@ struct Scoped Scoped(const Scope &scope, const Value &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine->current, v); + ptr->val = value_convert<T>(scope.engine->currentContext(), v); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -278,7 +278,7 @@ struct Scoped Scoped(const Scope &scope, const ReturnedValue &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine->current, QV4::Value::fromReturnedValue(v)); + ptr->val = value_convert<T>(scope.engine->currentContext(), QV4::Value::fromReturnedValue(v)); #ifndef QT_NO_DEBUG ++scope.size; #endif diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 25791cff61..4fd0569627 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -51,6 +51,7 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> +#include <private/qqmlengine_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> @@ -62,27 +63,41 @@ using namespace QV4; QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, ObjectRef qml) : FunctionObject(scope, scope->engine->id_eval) , qml(qml) + , qmlContext(0) { - vtbl = &static_vtbl; + Q_ASSERT(scope->inUse); + + setVTable(&static_vtbl); function = f; function->compilationUnit->ref(); needsActivation = function->needsActivation(); + + Scope s(scope); + ScopedValue protectThis(s, this); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(1)); - qmlContext = scope->engine->current->newQmlContext(this, qml); + qmlContext = scope->engine->currentContext()->newQmlContext(this, qml); scope->engine->popContext(); } QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, ObjectRef qml) : FunctionObject(scope, scope->engine->id_eval) , qml(qml) + , qmlContext(0) { - vtbl = &static_vtbl; + Q_ASSERT(scope->inUse); + + setVTable(&static_vtbl); function = 0; needsActivation = false; + + Scope s(scope); + ScopedValue protectThis(s, this); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(1)); - qmlContext = scope->engine->current->newQmlContext(this, qml); + qmlContext = scope->engine->currentContext()->newQmlContext(this, qml); scope->engine->popContext(); } @@ -110,7 +125,8 @@ void QmlBindingWrapper::markObjects(Managed *m, ExecutionEngine *e) if (wrapper->qml) wrapper->qml->mark(e); FunctionObject::markObjects(m, e); - wrapper->qmlContext->mark(); + if (wrapper->qmlContext) + wrapper->qmlContext->mark(e); } DEFINE_MANAGED_VTABLE(QmlBindingWrapper); @@ -124,7 +140,7 @@ struct CompilationUnitHolder : public QV4::Object , unit(unit) { unit->ref(); - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ~CompilationUnitHolder() { @@ -214,7 +230,7 @@ void Script::parse() return; QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); + QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); if (inheritContext) isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); @@ -226,7 +242,7 @@ void Script::parse() if (!vmFunction) { // ### FIX file/line number Scoped<Object> error(valueScope, v4->newSyntaxErrorObject(QStringLiteral("Syntax error"))); - v4->current->throwError(error); + v4->currentContext()->throwError(error); } } @@ -340,7 +356,7 @@ CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const } Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(engine->iselFactory->create(engine->executableAllocator, &module, &jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, &module, &jsGenerator)); isel->setUseFastLookups(false); return isel->compile(); } @@ -361,7 +377,7 @@ QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &scr QV4::Scope scope(engine); QV4::Script qmlScript(engine, scopeObject, script, QString()); - QV4::ExecutionContext *ctx = engine->current; + QV4::ExecutionContext *ctx = engine->currentContext(); qmlScript.parse(); QV4::ScopedValue result(scope); if (!scope.engine->hasException) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 26e4dcb8a2..8b0e31cb71 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -47,6 +47,7 @@ #include <private/qv4arrayobject_p.h> #include <private/qqmlengine_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4internalclass_p.h> #include <algorithm> @@ -167,14 +168,13 @@ class QQmlSequence : public QV4::Object Q_MANAGED public: QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) - : QV4::Object(engine->sequenceClass) + : QV4::Object(InternalClass::create(engine, &static_vtbl, engine->sequencePrototype.asObject())) , m_container(container) , m_object(0) , m_propertyIndex(-1) , m_isReference(false) { type = Type_QmlSequence; - vtbl = &static_vtbl; flags &= ~SimpleArray; QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); @@ -183,13 +183,12 @@ public: } QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) - : QV4::Object(engine->sequenceClass) + : QV4::Object(InternalClass::create(engine, &static_vtbl, engine->sequencePrototype.asObject())) , m_object(object) , m_propertyIndex(propertyIndex) , m_isReference(true) { type = Type_QmlSequence; - vtbl = &static_vtbl; flags &= ~SimpleArray; QV4::Scope scope(engine); QV4::ScopedObject protectThis(scope, this); @@ -207,7 +206,7 @@ public: { /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed get")); + generateWarning(engine()->currentContext(), QLatin1String("Index out of range during indexed get")); if (hasProperty) *hasProperty = false; return Encode::undefined(); @@ -238,7 +237,7 @@ public: /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed set")); + generateWarning(engine()->currentContext(), QLatin1String("Index out of range during indexed set")); return; } @@ -276,7 +275,7 @@ public: { /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed query")); + generateWarning(engine()->currentContext(), QLatin1String("Index out of range during indexed query")); return QV4::Attr_Invalid; } if (m_isReference) { diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 06a2603280..ee325db4c2 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -279,7 +279,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en s = properties->getIndexed(ii); serialize(data, s, engine); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); str = s; val = o->get(str); if (scope.hasException()) diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 0e43d03987..e5633eb06f 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -108,7 +108,6 @@ const ManagedVTable String::static_vtbl = markObjects, destroy, 0 /*collectDeletables*/, - hasInstance, get, getIndexed, put, @@ -150,7 +149,7 @@ ReturnedValue String::get(Managed *m, const StringRef name, bool *hasProperty) return Primitive::fromInt32(that->_text->size).asReturnedValue(); } PropertyAttributes attrs; - Property *pd = v4->stringClass->prototype->__getPropertyDescriptor__(name, &attrs); + Property *pd = v4->stringObjectClass->prototype->__getPropertyDescriptor__(name, &attrs); if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; @@ -158,7 +157,7 @@ ReturnedValue String::get(Managed *m, const StringRef name, bool *hasProperty) } if (hasProperty) *hasProperty = true; - return v4->stringClass->prototype->getValue(that, pd, attrs); + return v4->stringObjectClass->prototype->getValue(that, pd, attrs); } ReturnedValue String::getIndexed(Managed *m, uint index, bool *hasProperty) @@ -173,7 +172,7 @@ ReturnedValue String::getIndexed(Managed *m, uint index, bool *hasProperty) return Encode(engine->newString(that->toQString().mid(index, 1))); } PropertyAttributes attrs; - Property *pd = engine->stringClass->prototype->__getPropertyDescriptor__(index, &attrs); + Property *pd = engine->stringObjectClass->prototype->__getPropertyDescriptor__(index, &attrs); if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; @@ -181,7 +180,7 @@ ReturnedValue String::getIndexed(Managed *m, uint index, bool *hasProperty) } if (hasProperty) *hasProperty = true; - return engine->stringClass->prototype->getValue(that, pd, attrs); + return engine->stringObjectClass->prototype->getValue(that, pd, attrs); } void String::put(Managed *m, const StringRef name, const ValueRef value) @@ -252,24 +251,22 @@ bool String::isEqualTo(Managed *t, Managed *o) String::String(ExecutionEngine *engine, const QString &text) - : Managed(engine ? engine->emptyClass : 0), _text(const_cast<QString &>(text).data_ptr()) + : Managed(engine->stringClass), _text(const_cast<QString &>(text).data_ptr()) , identifier(0), stringHash(UINT_MAX) , largestSubLength(0) { _text->ref.ref(); len = _text->size; - vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } String::String(ExecutionEngine *engine, String *l, String *r) - : Managed(engine ? engine->emptyClass : 0) + : Managed(engine->stringClass) , left(l), right(r) , stringHash(UINT_MAX), largestSubLength(qMax(l->largestSubLength, r->largestSubLength)) , len(l->len + r->len) { - vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; @@ -360,7 +357,7 @@ void String::createHashValue() const // array indices get their number as hash value bool ok; - stringHash = toArrayIndex(ch, end, &ok); + stringHash = ::toArrayIndex(ch, end, &ok); if (ok) { subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; return; @@ -382,7 +379,7 @@ uint String::createHashValue(const QChar *ch, int length) // array indices get their number as hash value bool ok; - uint stringHash = toArrayIndex(ch, end, &ok); + uint stringHash = ::toArrayIndex(ch, end, &ok); if (ok) return stringHash; @@ -401,7 +398,7 @@ uint String::createHashValue(const char *ch, int length) // array indices get their number as hash value bool ok; - uint stringHash = toArrayIndex(ch, end, &ok); + uint stringHash = ::toArrayIndex(ch, end, &ok); if (ok) return stringHash; @@ -415,3 +412,9 @@ uint String::createHashValue(const char *ch, int length) return h; } + +uint String::toArrayIndex(const QString &str) +{ + bool ok; + return ::toArrayIndex(str.constData(), str.constData() + str.length(), &ok); +} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index bb6f1d2279..64e15b04c2 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -63,7 +63,7 @@ struct Q_QML_EXPORT String : public Managed { String() : Managed(0), _text(QStringData::sharedNull()), identifier(0) , stringHash(UINT_MAX), largestSubLength(0), len(0) - { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } + { type = Type_String; subtype = StringType_Unknown; } String(ExecutionEngine *engine, const QString &text); String(ExecutionEngine *engine, String *l, String *n); ~String() { @@ -140,6 +140,8 @@ struct Q_QML_EXPORT String : public Managed { return len; } + static uint toArrayIndex(const QString &str); + union { mutable QStringData *_text; mutable String *left; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index bff8f1f9cd..d468fb6b83 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -80,7 +80,7 @@ DEFINE_MANAGED_VTABLE(StringObject); StringObject::StringObject(InternalClass *ic) : Object(ic) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_StringObject; Scope scope(engine()); @@ -94,9 +94,9 @@ StringObject::StringObject(InternalClass *ic) } StringObject::StringObject(ExecutionEngine *engine, const ValueRef val) - : Object(engine->stringClass) + : Object(engine->stringObjectClass) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_StringObject; Scope scope(engine); @@ -125,13 +125,13 @@ bool StringObject::deleteIndexedProperty(Managed *m, uint index) Scope scope(v4); Scoped<StringObject> o(scope, m->asStringObject()); if (!o) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return false; } if (index < static_cast<uint>(o->value.stringValue()->toQString().length())) { - if (v4->current->strictMode) - v4->current->throwTypeError(); + if (v4->currentContext()->strictMode) + v4->currentContext()->throwTypeError(); return false; } return true; @@ -172,7 +172,7 @@ DEFINE_MANAGED_VTABLE(StringCtor); StringCtor::StringCtor(ExecutionContext *scope) : FunctionObject(scope, QStringLiteral("String")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ReturnedValue StringCtor::construct(Managed *m, CallData *callData) @@ -181,7 +181,7 @@ ReturnedValue StringCtor::construct(Managed *m, CallData *callData) Scope scope(v4); ScopedValue value(scope); if (callData->argc) - value = callData->args[0].toString(v4->current); + value = callData->args[0].toString(v4->currentContext()); else value = v4->newString(QString()); return Encode(v4->newStringObject(value)); @@ -193,7 +193,7 @@ ReturnedValue StringCtor::call(Managed *m, CallData *callData) Scope scope(v4); ScopedValue value(scope); if (callData->argc) - value = callData->args[0].toString(v4->current); + value = callData->args[0].toString(v4->currentContext()); else value = v4->newString(QString()); return value.asReturnedValue(); diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 4ae570c8dc..30f7e8cdb0 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -90,7 +90,7 @@ double Value::toNumberImpl() const if (isString()) return __qmljs_string_to_number(stringValue()->toQString()); { - ExecutionContext *ctx = objectValue()->internalClass->engine->current; + ExecutionContext *ctx = objectValue()->internalClass->engine->currentContext(); Scope scope(ctx); ScopedValue prim(scope, __qmljs_to_primitive(ValueRef::fromRawValue(this), NUMBER_HINT)); return prim->toNumber(); @@ -121,7 +121,7 @@ QString Value::toQStringNoThrow() const if (isString()) return stringValue()->toQString(); { - ExecutionContext *ctx = objectValue()->internalClass->engine->current; + ExecutionContext *ctx = objectValue()->internalClass->engine->currentContext(); Scope scope(ctx); ScopedValue ex(scope); bool caughtException = false; @@ -174,7 +174,7 @@ QString Value::toQString() const if (isString()) return stringValue()->toQString(); { - ExecutionContext *ctx = objectValue()->internalClass->engine->current; + ExecutionContext *ctx = objectValue()->internalClass->engine->currentContext(); Scope scope(ctx); ScopedValue prim(scope, __qmljs_to_primitive(ValueRef::fromRawValue(this), STRING_HINT)); return prim->toQString(); diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 470e8e206b..dfa4ac5775 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -56,7 +56,6 @@ VariantObject::VariantObject(InternalClass *ic) , ExecutionEngine::ScarceResourceData(QVariant()) , m_vmePropertyReferenceCount(0) { - vtbl = &static_vtbl; } VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value) @@ -64,7 +63,6 @@ VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value) , ExecutionEngine::ScarceResourceData(value) , m_vmePropertyReferenceCount(0) { - vtbl = &static_vtbl; if (isScarce()) engine->scarceResources.insert(this); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 72db469ee6..9d6540ebe9 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -320,6 +320,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, STOREVALUE(instr.result, __qmljs_get_qobject_property(context, VALUEPTR(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadQObjectProperty) + MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) + STOREVALUE(instr.result, __qmljs_get_attached_property(context, instr.attachedPropertiesId, instr.propertyIndex)); + MOTH_END_INSTR(LoadAttachedQObjectProperty) + MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; @@ -503,10 +507,19 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, __qmljs_construct_property(context, VALUEPTR(instr.base), runtimeStrings[instr.name], callData)); + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, __qmljs_construct_property(context, runtimeStrings[instr.name], callData)); MOTH_END_INSTR(CreateProperty) + MOTH_BEGIN_INSTR(ConstructPropertyLookup) + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, __qmljs_construct_property_lookup(context, instr.index, callData)); + MOTH_END_INSTR(ConstructPropertyLookup) + MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, args = %d, argc = %d", runtimeStrings[instr.name]->toQString().toUtf8().constData(), instr.args, instr.argc); Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); @@ -648,9 +661,9 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = context->callData->thisObject; MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlIdObject) - VALUE(instr.result) = __qmljs_get_id_object(static_cast<QV4::NoThrowContext*>(context), instr.id); - MOTH_END_INSTR(LoadQmlIdObject) + MOTH_BEGIN_INSTR(LoadQmlIdArray) + VALUE(instr.result) = __qmljs_get_id_array(static_cast<QV4::NoThrowContext*>(context)); + MOTH_END_INSTR(LoadQmlIdArray) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) VALUE(instr.result) = __qmljs_get_imported_scripts(static_cast<QV4::NoThrowContext*>(context)); @@ -664,6 +677,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = __qmljs_get_scope_object(static_cast<QV4::NoThrowContext*>(context)); MOTH_END_INSTR(LoadScopeObject) + MOTH_BEGIN_INSTR(LoadQmlSingleton) + VALUE(instr.result) = __qmljs_get_qml_singleton(static_cast<QV4::NoThrowContext*>(context), runtimeStrings[instr.name]); + MOTH_END_INSTR(LoadQmlSingleton) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 08bda0bce7..b0ea93e9fb 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -5,6 +5,7 @@ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS +win32:!wince*:!winrt:LIBS += -lshell32 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 MODULE_PLUGIN_TYPES = \ @@ -17,6 +18,9 @@ exists("qqml_enable_gcov") { QMAKE_DOCS = $$PWD/doc/qtqml.qdocconf +# 2415: variable "xx" of static storage duration was declared but never referenced +intel_icc: WERROR += -ww2415 + load(qt_module) HEADERS += qtqmlglobal.h \ diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index 321e6ccb41..012412b6b5 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -195,20 +195,6 @@ bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) return true; } -// Unicode stuff -static inline bool isUnicodeNonCharacter(uint ucs4) -{ - // Unicode has a couple of "non-characters" that one can use internally, - // but are not allowed to be used for text interchange. - // - // Those are the last two entries each Unicode Plane (U+FFFE, U+FFFF, - // U+1FFFE, U+1FFFF, etc.) as well as the entries between U+FDD0 and - // U+FDEF (inclusive) - - return (ucs4 & 0xfffe) == 0xfffe - || (ucs4 - 0xfdd0U) < 16; -} - QHashedStringRef QHashedStringRef::mid(int offset, int length) const { Q_ASSERT(offset < m_length); diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index b6c6fe840d..641209d1f3 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -467,7 +467,7 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } -static const int CurrentSingletonTypeRegistrationVersion = 2; +enum { QmlCurrentSingletonTypeRegistrationVersion = 2 }; template <typename T> inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *(*callback)(QQmlEngine *, QJSEngine *)) @@ -475,7 +475,7 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi QML_GETTYPENAMES QQmlPrivate::RegisterSingletonType api = { - CurrentSingletonTypeRegistrationVersion, + QmlCurrentSingletonTypeRegistrationVersion, uri, versionMajor, versionMinor, typeName, diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 557267d808..9e2fb07066 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -50,6 +50,7 @@ #include <private/qqmltrace_p.h> #include <private/qqmlexpression_p.h> #include <private/qqmlscriptstring_p.h> +#include <private/qqmlcontextwrapper_p.h> #include <QVariant> #include <QtCore/qdebug.h> @@ -85,7 +86,14 @@ QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt, Q_ASSERT(typeData); if (QQmlCompiledData *cdata = typeData->compiledData()) { - rv = new QQmlBinding(cdata->primitives.at(id), obj, ctxtdata, url, lineNumber, 0); + QV4::ExecutionEngine *v4 = engine->v4engine(); + QV4::Scope valueScope(v4); + QV4::ScopedObject scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(v4->v8Engine, ctxtdata, obj)); + QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject)); + QV4::ExecutionContext *qmlContext = wrapper->context(); + QV4::Function *runtimeFunction = cdata->compilationUnit->runtimeFunctions[cdata->customParserBindings[id]]; + QV4::ScopedValue function(valueScope, QV4::FunctionObject::creatScriptFunction(qmlContext, runtimeFunction)); + rv = new QQmlBinding(function, obj, ctxtdata, url, lineNumber, 0); } typeData->release(); diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 11dc873dd4..68160edf5e 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -89,8 +89,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QV4::ValueRef &function) : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), m_v8function(function), - m_line(-1), - m_column(-1), + m_line(USHRT_MAX), + m_column(USHRT_MAX), m_target(target), m_index(index), m_expressionFunctionValid(true), diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 9fcef176ad..76bf24fe6b 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -45,6 +45,7 @@ #include "qqmlcomponent_p.h" #include "qqmlcontext.h" #include "qqmlcontext_p.h" +#include "qqmlpropertymap.h" #ifdef QML_THREADED_VME_INTERPRETER #include "qqmlvme_p.h" #endif @@ -173,6 +174,27 @@ QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngi } } +template <typename T> +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void QQmlCompiledData::TypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = 0; + if (typePropertyCache) + mo = typePropertyCache->firstCppMetaObject(); + else if (type) + mo = type->metaObject(); + else + mo = component->rootPropertyCache->firstCppMetaObject(); + isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); +} void QQmlCompiledData::dumpInstructions() { diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 187274890b..2e208f2f3b 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -62,7 +62,6 @@ #include "qqmlglobal_p.h" #include "qqmlbinding_p.h" #include "qqmlabstracturlinterceptor.h" -#include "qqmlcodegenerator_p.h" #include <QDebug> #include <QPointF> @@ -848,6 +847,8 @@ bool QQmlCompiler::compile(QQmlEngine *engine, } } + ref.doDynamicTypeCheck(); + out->types << ref; } @@ -859,6 +860,19 @@ bool QQmlCompiler::compile(QQmlEngine *engine, if (componentStats) dumpStats(); Q_ASSERT(out->rootPropertyCache); + + // Any QQmlPropertyMap instances for example need to have their property cache removed, + // because the class is too dynamic and allows adding properties at any point at run-time. + for (int i = 0; i < output->types.count(); ++i) { + QQmlCompiledData::TypeReference &tr = output->types[i]; + if (!tr.typePropertyCache) + continue; + + if (tr.isFullyDynamicType) { + tr.typePropertyCache->release(); + tr.typePropertyCache = 0; + } + } } else { reset(out); } @@ -919,7 +933,7 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree) if (!jsModule->functions.isEmpty()) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Compiler::JSUnitGenerator jsUnitGenerator(jsModule.data()); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, jsModule.data(), &jsUnitGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, v4->executableAllocator, jsModule.data(), &jsUnitGenerator)); isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/true); output->compilationUnit = jsUnit; @@ -1227,6 +1241,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper) // Setup the synthesized meta object if necessary if (!obj->synthdata.isEmpty()) { + Q_ASSERT(!output->types.at(obj->type).isFullyDynamicType); Instruction::StoreMetaObject meta; meta.aliasData = output->indexForByteArray(obj->synthdata); meta.propertyCache = output->propertyCaches.count(); @@ -2682,14 +2697,42 @@ const QMetaObject *QQmlCompiler::resolveType(const QString& name) const return qmltype->metaObject(); } -int QQmlCompiler::bindingIdentifier(const Variant &value) +int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, const BindingContext &ctxt) { - return output->indexForString(value.asScript()); + JSBindingReference *reference = pool->New<JSBindingReference>(); + reference->expression = value; + reference->property = pool->New<Property>(); + reference->property->setName(name); + reference->value = 0; + reference->bindingContext = ctxt; + reference->bindingContext.owner++; + // Unfortunately this is required for example for PropertyChanges where the bindings + // will be executed in the dynamic scope of the target, so we can't resolve any lookups + // at run-time. + reference->disableLookupAcceleration = true; + + const int id = output->customParserBindings.count(); + output->customParserBindings.append(0); // Filled in later. + reference->customParserBindingsIndex = id; + + compileState->totalBindingsCount++; + compileState->bindings.prepend(reference); + + return id; } // Ensures that the dynamic meta specification on obj is valid bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj) { + if (output->types[obj->type].isFullyDynamicType) { + if (!obj->dynamicProperties.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new properties.")); + if (!obj->dynamicSignals.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new signals.")); + if (!obj->dynamicSlots.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully Dynamic types cannot declare new functions.")); + } + bool seenDefaultProperty = false; // We use a coarse grain, 31 bit hash to check if there are duplicates. @@ -3644,11 +3687,14 @@ bool QQmlCompiler::completeComponentBuild() } ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[b->bindingContext.object]; - cd->functionsToCompile.append(node); + QtQml::CompiledFunctionOrExpression f; + f.node = node; + f.name = binding.property->name().toString().prepend(QStringLiteral("expression for ")); + f.disableAcceleratedLookups = binding.disableLookupAcceleration; + cd->functionsToCompile.append(f); binding.compiledIndex = cd->functionsToCompile.count() - 1; - cd->expressionNames.insert(binding.compiledIndex, binding.property->name().toString().prepend(QStringLiteral("expression for "))); - if (componentStats) + if (componentStats && b->value) componentStats->componentStat.scriptBindings.append(b->value->location); } @@ -3656,7 +3702,7 @@ bool QQmlCompiler::completeComponentBuild() const QString &sourceCode = jsEngine->code(); AST::UiProgram *qmlRoot = parser.qmlRoot(); - JSCodeGen jsCodeGen(enginePrivate, unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot, output->importCache); + JSCodeGen jsCodeGen(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot, output->importCache); JSCodeGen::ObjectIdMapping idMapping; if (compileState->ids.count() > 0) { @@ -3679,7 +3725,7 @@ bool QQmlCompiler::completeComponentBuild() jsCodeGen.beginObjectScope(scopeObject->metatype); - cd->runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(cd->functionsToCompile, cd->expressionNames); + cd->runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(cd->functionsToCompile); QList<QQmlError> errors = jsCodeGen.errors(); if (!errors.isEmpty()) { exceptions << errors; @@ -3697,6 +3743,10 @@ bool QQmlCompiler::completeComponentBuild() for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { JSBindingReference &binding = *b; binding.compiledIndex = compileState->jsCompileData[binding.bindingContext.object].runtimeFunctionIndices[binding.compiledIndex]; + if (!binding.value) { // Must be a binding requested from custom parser + Q_ASSERT(binding.customParserBindingsIndex >= 0 && binding.customParserBindingsIndex < output->customParserBindings.count()); + output->customParserBindings[binding.customParserBindingsIndex] = binding.compiledIndex; + } } } diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 3ca4566e41..516f6653ca 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -62,6 +62,7 @@ #include "qqmlpropertycache_p.h" #include "qqmltypenamecache_p.h" #include "qqmltypeloader_p.h" +#include <private/qqmlcodegenerator_p.h> #include "private/qv4identifier_p.h" #include <private/qqmljsastfwd_p.h> @@ -105,6 +106,7 @@ public: : type(0), typePropertyCache(0), component(0) , majorVersion(0) , minorVersion(0) + , isFullyDynamicType(false) {} QQmlType *type; @@ -113,9 +115,14 @@ public: int majorVersion; int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; QQmlPropertyCache *propertyCache() const; QQmlPropertyCache *createPropertyCache(QQmlEngine *); + + void doDynamicTypeCheck(); }; // --- old compiler: QList<TypeReference> types; @@ -150,6 +157,7 @@ public: // index in first hash is component index, hash inside maps from object index in that scope to integer id QHash<int, QHash<int, int> > objectIndexToIdPerComponent; QHash<int, int> objectIndexToIdForRoot; + QVector<int> customParserBindings; // index is binding identifier, value is compiled function index. bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } bool isCompositeType() const { return !datas.at(qmlUnit->indexOfRootObject).isEmpty(); } @@ -169,7 +177,7 @@ public: int addInstruction(const QQmlInstructionData<Instr> &data) { QQmlInstruction genericInstr; - QQmlInstructionMeta<Instr>::setData(genericInstr, data); + QQmlInstructionMeta<Instr>::setDataNoCommon(genericInstr, data); return addInstructionHelper(static_cast<QQmlInstruction::Type>(Instr), genericInstr); } int nextInstructionIndex(); @@ -224,14 +232,15 @@ namespace QQmlCompilerTypes { struct JSBindingReference : public QQmlPool::Class, public BindingReference { - JSBindingReference() : nextReference(0) {} + JSBindingReference() : disableLookupAcceleration(false), nextReference(0) {} QQmlScript::Variant expression; QQmlScript::Property *property; QQmlScript::Value *value; int compiledIndex : 16; - int sharedIndex : 16; + int customParserBindingsIndex : 15; + int disableLookupAcceleration: 1; BindingContext bindingContext; @@ -312,10 +321,9 @@ namespace QQmlCompilerTypes { QList<CompiledMetaMethod> compiledMetaMethods; struct PerObjectCompileData { - QList<QQmlJS::AST::Node*> functionsToCompile; + QList<QtQml::CompiledFunctionOrExpression> functionsToCompile; QVector<int> runtimeFunctionIndices; QVector<CompiledMetaMethod> compiledMetaMethods; - QHash<int, QString> expressionNames; }; QHash<QQmlScript::Object *, PerObjectCompileData> jsCompileData; }; @@ -340,7 +348,7 @@ public: int evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const; // for QQmlCustomParser::evaluateEnum const QMetaObject *resolveType(const QString& name) const; // for QQmlCustomParser::resolveType - int bindingIdentifier(const QQmlScript::Variant& value); // for QQmlCustomParser::bindingIndex + int bindingIdentifier(const QString &name, const QQmlScript::Variant& value, const QQmlCompilerTypes::BindingContext &ctxt); // for QQmlCustomParser::bindingIndex private: typedef QQmlCompiledData::Instruction Instruction; diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 2973944215..4a71c1a7e0 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -52,7 +52,6 @@ #include "qqmlbinding_p.h" #include "qqmlglobal_p.h" #include "qqmlscript_p.h" -#include <private/qqmlprofilerservice_p.h> #include <private/qqmlenginedebugservice_p.h> #include "qqmlincubator.h" #include "qqmlincubator_p.h" @@ -884,11 +883,6 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); - if (enginePriv->inProgressCreations == 0) { - // only track root, since further ones might not be properly nested - profiler = new QQmlObjectCreatingProfiler(); - } - enginePriv->inProgressCreations++; state.errors.clear(); state.completePending = true; @@ -924,13 +918,6 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) if (!context->isInternal) context->asQQmlContextPrivate()->instances.append(rv); QQmlEngineDebugService::instance()->objectCreated(engine, rv); - - if (profiler && profiler->enabled) { - profiler->setTypeName(buildTypeNameForDebug(rv->metaObject())); - QQmlData *data = QQmlData::get(rv); - Q_ASSERT(data); - profiler->setLocation(cc->url, data->lineNumber, data->columnNumber); - } } return rv; @@ -995,9 +982,6 @@ void QQmlComponentPrivate::completeCreate() if (state.completePending) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); complete(ep, &state); - - delete profiler; - profiler = 0; } if (depthIncreased) { @@ -1507,7 +1491,7 @@ QmlIncubatorObject::QmlIncubatorObject(QV8Engine *engine, QQmlIncubator::Incubat { incubator.reset(new QQmlComponentIncubator(this, m)); v8 = engine; - vtbl = &static_vtbl; + setVTable(&static_vtbl); valuemap = QV4::Primitive::undefinedValue(); qmlGlobal = QV4::Primitive::undefinedValue(); @@ -1562,7 +1546,7 @@ void QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) QV4::ScopedFunctionObject f(scope, m_statusChanged); if (f) { - QV4::ExecutionContext *ctx = scope.engine->current; + QV4::ExecutionContext *ctx = scope.engine->currentContext(); QV4::ScopedCallData callData(scope, 1); callData->thisObject = this; callData->args[0] = QV4::Primitive::fromUInt32(s); diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index d9a2427cd5..8bf4005dd6 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -61,7 +61,6 @@ #include "qqmlvme_p.h" #include "qqmlerror.h" #include "qqml.h" -#include <private/qqmlprofilerservice_p.h> #include <private/qqmlobjectcreator_p.h> #include <QtCore/QString> @@ -85,7 +84,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public public: QQmlComponentPrivate() - : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), profiler(0), depthIncreased(false) {} + : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), depthIncreased(false) {} void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); @@ -131,7 +130,6 @@ public: QQmlEngine *engine; QQmlGuardedContextData creationContext; - QQmlObjectCreatingProfiler *profiler; bool depthIncreased; void clear(); diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index 406826a6f6..b3c2105e68 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -53,6 +53,7 @@ #include <private/qv4compileddata_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmllistwrapper_p.h> +#include <private/qjsvalue_p.h> QT_BEGIN_NAMESPACE @@ -63,9 +64,9 @@ DEFINE_MANAGED_VTABLE(QmlContextWrapper); QmlContextWrapper::QmlContextWrapper(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext) : Object(QV8Engine::getV4(engine)), v8(engine), readOnly(true), ownsContext(ownsContext), isNullWrapper(false), - context(context), scopeObject(scopeObject) + context(context), scopeObject(scopeObject), idObjectsWrapper(0) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QmlContextWrapper::~QmlContextWrapper() @@ -136,7 +137,7 @@ ReturnedValue QmlContextWrapper::get(Managed *m, const StringRef name, bool *has QV4::Scope scope(v4); QmlContextWrapper *resource = m->as<QmlContextWrapper>(); if (!resource) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); // In V8 the JS global object would come _before_ the QML global object, // so simulate that here. @@ -245,7 +246,7 @@ ReturnedValue QmlContextWrapper::get(Managed *m, const StringRef name, bool *has // Search scope object if (scopeObject) { bool hasProp = false; - QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4->current, context, scopeObject, + QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4->currentContext(), context, scopeObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, &hasProp)); if (hasProp) { if (hasProperty) @@ -259,7 +260,7 @@ ReturnedValue QmlContextWrapper::get(Managed *m, const StringRef name, bool *has // Search context object if (context->contextObject) { bool hasProp = false; - result = QV4::QObjectWrapper::getQmlProperty(v4->current, context, context->contextObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, &hasProp); + result = QV4::QObjectWrapper::getQmlProperty(v4->currentContext(), context, context->contextObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, &hasProp); if (hasProp) { if (hasProperty) *hasProperty = true; @@ -283,7 +284,7 @@ void QmlContextWrapper::put(Managed *m, const StringRef name, const ValueRef val return; QV4::Scoped<QmlContextWrapper> wrapper(scope, m->as<QmlContextWrapper>()); if (!wrapper) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return; } @@ -291,8 +292,8 @@ void QmlContextWrapper::put(Managed *m, const StringRef name, const ValueRef val if (wrapper && wrapper->readOnly) { QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); - Scoped<String> e(scope, v4->current->engine->newString(error)); - v4->current->throwError(e); + Scoped<String> e(scope, v4->currentContext()->engine->newString(error)); + v4->currentContext()->throwError(e); return; } @@ -326,13 +327,13 @@ void QmlContextWrapper::put(Managed *m, const StringRef name, const ValueRef val // Search scope object if (scopeObject && - QV4::QObjectWrapper::setQmlProperty(v4->current, context, scopeObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, value)) + QV4::QObjectWrapper::setQmlProperty(v4->currentContext(), context, scopeObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, value)) return; scopeObject = 0; // Search context object if (context->contextObject && - QV4::QObjectWrapper::setQmlProperty(v4->current, context, context->contextObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, value)) + QV4::QObjectWrapper::setQmlProperty(v4->currentContext(), context, context->contextObject, name.getPointer(), QV4::QObjectWrapper::CheckRevision, value)) return; context = context->parent; @@ -343,7 +344,7 @@ void QmlContextWrapper::put(Managed *m, const StringRef name, const ValueRef val if (wrapper->readOnly) { QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); - v4->current->throwError(error); + v4->currentContext()->throwError(error); return; } @@ -355,6 +356,14 @@ void QmlContextWrapper::destroy(Managed *that) static_cast<QmlContextWrapper *>(that)->~QmlContextWrapper(); } +void QmlContextWrapper::markObjects(Managed *m, ExecutionEngine *engine) +{ + QmlContextWrapper *This = static_cast<QmlContextWrapper*>(m); + if (This->idObjectsWrapper) + This->idObjectsWrapper->mark(engine); + Object::markObjects(m, engine); +} + void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const CompiledData::Function *compiledFunction) { // Let the caller check and avoid the function call :) @@ -395,4 +404,72 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C } +ReturnedValue QmlContextWrapper::idObjectsArray() +{ + if (!idObjectsWrapper) { + ExecutionEngine *v4 = engine(); + idObjectsWrapper = new (v4->memoryManager) QQmlIdObjectsArray(v4, this); + } + return idObjectsWrapper->asReturnedValue(); +} + +ReturnedValue QmlContextWrapper::qmlSingletonWrapper(const StringRef &name) +{ + if (!context->imports) + return Encode::undefined(); + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(name); + + Q_ASSERT(r.isValid()); + Q_ASSERT(r.type); + Q_ASSERT(r.type->isSingleton()); + + QQmlEngine *e = v8->engine(); + QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); + siinfo->init(e); + + if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) + return QV4::QObjectWrapper::wrap(engine(), qobjectSingleton); + return QJSValuePrivate::get(siinfo->scriptApi(e))->getValue(engine()); +} + +DEFINE_MANAGED_VTABLE(QQmlIdObjectsArray); + +QQmlIdObjectsArray::QQmlIdObjectsArray(ExecutionEngine *engine, QmlContextWrapper *contextWrapper) + : Object(engine) + , contextWrapper(contextWrapper) +{ + setVTable(&static_vtbl); +} + +ReturnedValue QQmlIdObjectsArray::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + QQmlIdObjectsArray *This = static_cast<QQmlIdObjectsArray*>(m); + QQmlContextData *context = This->contextWrapper->getContext(); + if (!context) { + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); + } + if (index >= (uint)context->idValueCount) { + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); + } + + ExecutionEngine *v4 = m->engine(); + QQmlEnginePrivate *ep = v4->v8Engine->engine() ? QQmlEnginePrivate::get(v4->v8Engine->engine()) : 0; + if (ep) + ep->captureProperty(&context->idValues[index].bindings); + + return QObjectWrapper::wrap(This->engine(), context->idValues[index].data()); +} + +void QQmlIdObjectsArray::markObjects(Managed *that, ExecutionEngine *engine) +{ + QQmlIdObjectsArray *This = static_cast<QQmlIdObjectsArray*>(that); + This->contextWrapper->mark(engine); + Object::markObjects(that, engine); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index d85f440b15..89ace7090c 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -69,6 +69,8 @@ namespace CompiledData { struct Function; } +struct QQmlIdObjectsArray; + struct Q_QML_EXPORT QmlContextWrapper : Object { Q_MANAGED @@ -90,9 +92,12 @@ struct Q_QML_EXPORT QmlContextWrapper : Object static ReturnedValue get(Managed *m, const StringRef name, bool *hasProperty); static void put(Managed *m, const StringRef name, const ValueRef value); static void destroy(Managed *that); + static void markObjects(Managed *m, ExecutionEngine *engine); static void registerQmlDependencies(ExecutionEngine *context, const CompiledData::Function *compiledFunction); + ReturnedValue idObjectsArray(); + ReturnedValue qmlSingletonWrapper(const StringRef &name); QV8Engine *v8; // ### temporary, remove bool readOnly; @@ -101,6 +106,19 @@ struct Q_QML_EXPORT QmlContextWrapper : Object QQmlGuardedContextData context; QPointer<QObject> scopeObject; +private: + QQmlIdObjectsArray *idObjectsWrapper; +}; + +struct QQmlIdObjectsArray : public Object +{ + Q_MANAGED + QQmlIdObjectsArray(ExecutionEngine *engine, QmlContextWrapper *contextWrapper); + + static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static void markObjects(Managed *that, ExecutionEngine *engine); + + QmlContextWrapper *contextWrapper; }; } diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index eba2e14e51..19e49009ce 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -313,8 +313,7 @@ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const */ QQmlBinding::Identifier QQmlCustomParser::bindingIdentifier(const QQmlScript::Variant &value, const QString& name) { - Q_UNUSED(name); - return compiler->bindingIdentifier(value); + return compiler->bindingIdentifier(name, value, QQmlCompilerTypes::BindingContext(object)); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 621b3d3c2e..982156ea15 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE template <class Key, class T> class QHash; +class QQmlEngine; class QQmlGuardImpl; class QQmlCompiledData; class QQmlAbstractBinding; @@ -222,6 +223,8 @@ public: static inline void flushPendingBinding(QObject *, int coreIndex); + static void ensurePropertyCache(QQmlEngine *engine, QObject *object); + private: // For attachedProperties mutable QQmlDataExtended *extendedData; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 1320f51d25..f8e5ad5874 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -101,6 +101,9 @@ #ifdef Q_OS_WIN // for %APPDATA% #include <qt_windows.h> +# if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +# include <shlobj.h> +# endif #include <qlibrary.h> #include <windows.h> @@ -556,7 +559,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) workerScriptEngine(0), activeVME(0), activeObjectCreator(0), networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), - scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), + scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { useNewCompiler = qmlUseNewCompiler(); @@ -1339,14 +1342,6 @@ void qmlExecuteDeferred(QObject *object) QQmlData *data = QQmlData::get(object); if (data && data->deferredData && !data->wasDeleted(object)) { - QQmlObjectCreatingProfiler prof; - if (prof.enabled) { - QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); - prof.setTypeName(type ? type->qmlTypeName() - : QString::fromUtf8(object->metaObject()->className())); - if (data->outerContext) - prof.setLocation(data->outerContext->url, data->lineNumber, data->columnNumber); - } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); QQmlComponentPrivate::ConstructionState state; @@ -1734,6 +1729,16 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) QQmlData_setBit(this, obj, coreIndex * 2 + 1); } +void QQmlData::ensurePropertyCache(QQmlEngine *engine, QObject *object) +{ + Q_ASSERT(engine); + QQmlData *ddata = QQmlData::get(object, /*create*/true); + if (ddata->propertyCache) + return; + ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object); + if (ddata->propertyCache) ddata->propertyCache->addref(); +} + void QQmlEnginePrivate::sendQuit() { Q_Q(QQmlEngine); @@ -2283,6 +2288,28 @@ bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const return typeLoader.isScriptLoaded(url); } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +// Normalize a file name using Shell API. As opposed to converting it +// to a short 8.3 name and back, this also works for drives where 8.3 notation +// is disabled (see 8dot3name options of fsutil.exe). +static inline QString shellNormalizeFileName(const QString &name) +{ + const QString nativeSeparatorName(QDir::toNativeSeparators(name)); + const LPCTSTR nameC = reinterpret_cast<LPCTSTR>(nativeSeparatorName.utf16()); + PIDLIST_ABSOLUTE file; + if (FAILED(SHParseDisplayName(nameC, NULL, &file, 0, NULL))) + return name; + TCHAR buffer[MAX_PATH]; + if (!SHGetPathFromIDList(file, buffer)) + return name; + QString canonicalName = QString::fromWCharArray(buffer); + // Upper case drive letter + if (canonicalName.size() > 2 && canonicalName.at(1) == QLatin1Char(':')) + canonicalName[0] = canonicalName.at(0).toUpper(); + return QDir::cleanPath(canonicalName); +} +#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT + bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) { #if defined(Q_OS_MAC) || defined(Q_OS_WIN) @@ -2292,14 +2319,7 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */) #if defined(Q_OS_MAC) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT) const QString canonical = info.canonicalFilePath(); #elif defined(Q_OS_WIN) - wchar_t buffer[1024]; - - DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); - if (rv == 0 || rv >= 1024) return true; - rv = ::GetLongPathName(buffer, buffer, 1024); - if (rv == 0 || rv >= 1024) return true; - - const QString canonical = QString::fromWCharArray(buffer); + const QString canonical = shellNormalizeFileName(absolute); #endif const int absoluteLength = absolute.length(); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 5a2d6c4e00..19eb320fbe 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -186,8 +186,8 @@ public: void referenceScarceResources(); void dereferenceScarceResources(); - QQmlTypeLoader typeLoader; QQmlImportDatabase importDatabase; + QQmlTypeLoader typeLoader; QString offlineStoragePath; @@ -257,6 +257,7 @@ public: inline static QQmlEnginePrivate *get(QQmlContext *c); inline static QQmlEnginePrivate *get(QQmlContextData *c); inline static QQmlEngine *get(QQmlEnginePrivate *p); + inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e); static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor); @@ -516,7 +517,17 @@ QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) { Q_ASSERT(p); - return p->q_func(); + return p->q_func(); +} + +QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) +{ + if (!e->v8Engine) + return 0; + QQmlEngine *qmlEngine = e->v8Engine->engine(); + if (!qmlEngine) + return 0; + return get(qmlEngine); } void QQmlEnginePrivate::captureProperty(QQmlNotifier *n) diff --git a/src/qml/qml/qqmlinstruction_p.h b/src/qml/qml/qqmlinstruction_p.h index 150ee8df19..bda8c3db0d 100644 --- a/src/qml/qml/qqmlinstruction_p.h +++ b/src/qml/qml/qqmlinstruction_p.h @@ -536,7 +536,11 @@ struct QQmlInstructionMeta { typedef QQmlInstruction::instr_##FMT DataType; \ static const DataType &data(const QQmlInstruction &instr) { return instr.FMT; } \ static void setData(QQmlInstruction &instr, const DataType &v) { memcpy(&instr.FMT, &v, Size); } \ - }; + static void setDataNoCommon(QQmlInstruction &instr, const DataType &v) \ + { memcpy(reinterpret_cast<char *>(&instr.FMT) + sizeof(QQmlInstruction::instr_common), \ + reinterpret_cast<const char *>(&v) + sizeof(QQmlInstruction::instr_common), \ + Size - sizeof(QQmlInstruction::instr_common)); } \ + }; FOR_EACH_QML_INSTR(QML_INSTR_META_TEMPLATE); #undef QML_INSTR_META_TEMPLATE diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 3fd0003656..499ade1ca5 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -155,7 +155,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4); QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(ctx->engine, scopeObject())); @@ -294,7 +294,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::Scope scope(v4); QV4::ScopedObject qmlScopeObject(scope, QV4::QmlContextWrapper::qmlScope(ep->v8engine(), ctxt, scopeObject)); @@ -328,7 +328,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, Q QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::Scope scope(v4); QV4::ScopedObject qmlScopeObject(scope, QV4::QmlContextWrapper::qmlScope(ep->v8engine(), ctxt, qmlScope)); diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 73dc3192aa..7b975c2cc8 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -56,7 +56,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine) : Object(QV8Engine::getV4(engine)), v8(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); flags &= ~SimpleArray; } @@ -106,7 +106,7 @@ ReturnedValue QmlListWrapper::get(Managed *m, const StringRef name, bool *hasPro QV4::ExecutionEngine *v4 = m->engine(); QmlListWrapper *w = m->as<QmlListWrapper>(); if (!w) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); if (name->equals(v4->id_length) && !w->object.isNull()) { quint32 count = w->property.count ? w->property.count(&w->property) : 0; @@ -127,7 +127,7 @@ ReturnedValue QmlListWrapper::getIndexed(Managed *m, uint index, bool *hasProper QV4::ExecutionEngine *e = m->engine(); QmlListWrapper *w = m->as<QmlListWrapper>(); if (!w) - return e->current->throwTypeError(); + return e->currentContext()->throwTypeError(); quint32 count = w->property.count ? w->property.count(&w->property) : 0; if (index < count && w->property.at) diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 5e8130f407..36e0da5b60 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -61,7 +61,7 @@ public: QQmlLocaleData(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); type = Type_Object; } @@ -872,7 +872,7 @@ QV4::ReturnedValue QQmlLocale::locale(QV8Engine *v8engine, const QString &locale void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine) { - engine->stringClass->prototype->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); + engine->stringObjectClass->prototype->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); } QV4::ReturnedValue QQmlLocale::method_localeCompare(QV4::CallContext *ctx) diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/qml/qqmlmemoryprofiler.cpp index e7b6653532..d93276fc17 100644 --- a/src/qml/qml/qqmlmemoryprofiler.cpp +++ b/src/qml/qml/qqmlmemoryprofiler.cpp @@ -100,8 +100,7 @@ static bool openLibrary() QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) : pushed(false) { if (openLibrary() && memprofile_is_enabled()) { - const char *location = url.path().toUtf8().constData(); - memprofile_push_location(location, 0); + memprofile_push_location(url.path().toUtf8().constData(), 0); pushed = true; } } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 342d1dc69c..ed0c0afd6f 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -225,17 +225,21 @@ public: void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle()); - v4->pushGlobalContext(); if (scriptCallback && scriptApi(e).isUndefined()) { + v4->pushGlobalContext(); setScriptApi(e, scriptCallback(e, e)); + v4->popContext(); } else if (qobjectCallback && !qobjectApi(e)) { + v4->pushGlobalContext(); setQObjectApi(e, qobjectCallback(e, e)); + v4->popContext(); } else if (!url.isEmpty() && !qobjectApi(e)) { + v4->pushGlobalContext(); QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); QObject *o = component.create(); setQObjectApi(e, o); + v4->popContext(); } - v4->popContext(); } void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 85d2fe41d9..6eda55e35b 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2371,13 +2371,12 @@ void QQmlTypeData::compile() // Compile JS binding expressions and signal handlers - JSCodeGen jsCodeGen(enginePrivate, finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, m_compiledData->importCache); - QHash<int, QString> expressionNames; // ### TODO - const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions, expressionNames); + JSCodeGen jsCodeGen(finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, m_compiledData->importCache); + const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions); QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false); @@ -2806,7 +2805,7 @@ QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *pare QV4::ScopedValue qmlglobal(scope, QV4::QmlContextWrapper::qmlScope(v8engine, ctxt, 0)); QV4::QmlContextWrapper::takeContextOwnership(qmlglobal); - QV4::ExecutionContext *ctx = QV8Engine::getV4(v8engine)->current; + QV4::ExecutionContext *ctx = QV8Engine::getV4(v8engine)->currentContext(); m_program->qml = qmlglobal; m_program->run(); if (scope.engine->hasException) { diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 258442bc1d..9c350a54a5 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -60,7 +60,7 @@ QmlTypeWrapper::QmlTypeWrapper(QV8Engine *engine) : Object(QV8Engine::getV4(engine)), v8(engine), mode(IncludeEnums), type(0), typeNamespace(0), importNamespace(0) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QmlTypeWrapper::~QmlTypeWrapper() @@ -126,7 +126,7 @@ ReturnedValue QmlTypeWrapper::get(Managed *m, const StringRef name, bool *hasPro Scoped<QmlTypeWrapper> w(scope, m->as<QmlTypeWrapper>()); if (!w) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); if (hasProperty) @@ -165,7 +165,7 @@ ReturnedValue QmlTypeWrapper::get(Managed *m, const StringRef name, bool *hasPro } // check for property. - return QV4::QObjectWrapper::getQmlProperty(v4->current, context, qobjectSingleton, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, hasProperty); + return QV4::QObjectWrapper::getQmlProperty(v4->currentContext(), context, qobjectSingleton, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, hasProperty); } else if (!siinfo->scriptApi(e).isUndefined()) { // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. QV4::ScopedObject o(scope, QJSValuePrivate::get(siinfo->scriptApi(e))->getValue(v4)); @@ -188,7 +188,7 @@ ReturnedValue QmlTypeWrapper::get(Managed *m, const StringRef name, bool *hasPro } else if (w->object) { QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); if (ao) - return QV4::QObjectWrapper::getQmlProperty(v4->current, context, ao, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, hasProperty); + return QV4::QObjectWrapper::getQmlProperty(v4->currentContext(), context, ao, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, hasProperty); // Fall through to base implementation } @@ -236,7 +236,7 @@ void QmlTypeWrapper::put(Managed *m, const StringRef name, const ValueRef value) if (v4->hasException) return; if (!w) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return; } @@ -249,7 +249,7 @@ void QmlTypeWrapper::put(Managed *m, const StringRef name, const ValueRef value) QObject *object = w->object; QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); if (ao) - QV4::QObjectWrapper::setQmlProperty(v4->current, context, ao, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value); + QV4::QObjectWrapper::setQmlProperty(v4->currentContext(), context, ao, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value); } else if (type && type->isSingleton()) { QQmlEngine *e = v8engine->engine(); QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo(); @@ -257,12 +257,12 @@ void QmlTypeWrapper::put(Managed *m, const StringRef name, const ValueRef value) QObject *qobjectSingleton = siinfo->qobjectApi(e); if (qobjectSingleton) { - QV4::QObjectWrapper::setQmlProperty(v4->current, context, qobjectSingleton, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value); + QV4::QObjectWrapper::setQmlProperty(v4->currentContext(), context, qobjectSingleton, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision, value); } else if (!siinfo->scriptApi(e).isUndefined()) { QV4::ScopedObject apiprivate(scope, QJSValuePrivate::get(siinfo->scriptApi(e))->value); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - v4->current->throwError(error); + v4->currentContext()->throwError(error); return; } else { apiprivate->put(name, value); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index c55b86b55a..bd44dfb0cf 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -169,8 +169,8 @@ public: class Q_QML_PRIVATE_EXPORT QQmlPointFValueType : public QQmlValueTypeBase<QPointF> { - Q_PROPERTY(qreal x READ x WRITE setX) - Q_PROPERTY(qreal y READ y WRITE setY) + Q_PROPERTY(qreal x READ x WRITE setX FINAL) + Q_PROPERTY(qreal y READ y WRITE setY FINAL) Q_OBJECT public: QQmlPointFValueType(QObject *parent = 0); @@ -185,8 +185,8 @@ public: class Q_QML_PRIVATE_EXPORT QQmlPointValueType : public QQmlValueTypeBase<QPoint> { - Q_PROPERTY(int x READ x WRITE setX) - Q_PROPERTY(int y READ y WRITE setY) + Q_PROPERTY(int x READ x WRITE setX FINAL) + Q_PROPERTY(int y READ y WRITE setY FINAL) Q_OBJECT public: QQmlPointValueType(QObject *parent = 0); @@ -201,8 +201,8 @@ public: class Q_QML_PRIVATE_EXPORT QQmlSizeFValueType : public QQmlValueTypeBase<QSizeF> { - Q_PROPERTY(qreal width READ width WRITE setWidth) - Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_PROPERTY(qreal width READ width WRITE setWidth FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight FINAL) Q_OBJECT public: QQmlSizeFValueType(QObject *parent = 0); @@ -217,8 +217,8 @@ public: class Q_QML_PRIVATE_EXPORT QQmlSizeValueType : public QQmlValueTypeBase<QSize> { - Q_PROPERTY(int width READ width WRITE setWidth) - Q_PROPERTY(int height READ height WRITE setHeight) + Q_PROPERTY(int width READ width WRITE setWidth FINAL) + Q_PROPERTY(int height READ height WRITE setHeight FINAL) Q_OBJECT public: QQmlSizeValueType(QObject *parent = 0); @@ -233,10 +233,10 @@ public: class Q_QML_PRIVATE_EXPORT QQmlRectFValueType : public QQmlValueTypeBase<QRectF> { - Q_PROPERTY(qreal x READ x WRITE setX) - Q_PROPERTY(qreal y READ y WRITE setY) - Q_PROPERTY(qreal width READ width WRITE setWidth) - Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_PROPERTY(qreal x READ x WRITE setX FINAL) + Q_PROPERTY(qreal y READ y WRITE setY FINAL) + Q_PROPERTY(qreal width READ width WRITE setWidth FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight FINAL) Q_OBJECT public: QQmlRectFValueType(QObject *parent = 0); @@ -256,10 +256,10 @@ public: class Q_QML_PRIVATE_EXPORT QQmlRectValueType : public QQmlValueTypeBase<QRect> { - Q_PROPERTY(int x READ x WRITE setX) - Q_PROPERTY(int y READ y WRITE setY) - Q_PROPERTY(int width READ width WRITE setWidth) - Q_PROPERTY(int height READ height WRITE setHeight) + Q_PROPERTY(int x READ x WRITE setX FINAL) + Q_PROPERTY(int y READ y WRITE setY FINAL) + Q_PROPERTY(int width READ width WRITE setWidth FINAL) + Q_PROPERTY(int height READ height WRITE setHeight FINAL) Q_OBJECT public: QQmlRectValueType(QObject *parent = 0); @@ -282,11 +282,11 @@ class Q_QML_PRIVATE_EXPORT QQmlEasingValueType : public QQmlValueTypeBase<QEasin Q_OBJECT Q_ENUMS(Type) - Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType) - Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude) - Q_PROPERTY(qreal overshoot READ overshoot WRITE setOvershoot) - Q_PROPERTY(qreal period READ period WRITE setPeriod) - Q_PROPERTY(QVariantList bezierCurve READ bezierCurve WRITE setBezierCurve) + Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType FINAL) + Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL) + Q_PROPERTY(qreal overshoot READ overshoot WRITE setOvershoot FINAL) + Q_PROPERTY(qreal period READ period WRITE setPeriod FINAL) + Q_PROPERTY(QVariantList bezierCurve READ bezierCurve WRITE setBezierCurve FINAL) public: enum Type { Linear = QEasingCurve::Linear, diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index d733694923..50d7cbcc5e 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -79,7 +79,7 @@ QmlValueTypeWrapper::QmlValueTypeWrapper(QV8Engine *engine, ObjectType objectTyp : Object(QV8Engine::getV4(engine)), objectType(objectType) { v8 = engine; - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QmlValueTypeWrapper::~QmlValueTypeWrapper() @@ -209,7 +209,7 @@ PropertyAttributes QmlValueTypeWrapper::query(const Managed *m, StringRef name) const QmlValueTypeWrapper *r = m->as<const QmlValueTypeWrapper>(); QV4::ExecutionEngine *v4 = m->engine(); if (!r) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return PropertyAttributes(); } @@ -273,7 +273,7 @@ ReturnedValue QmlValueTypeWrapper::get(Managed *m, const StringRef name, bool *h QmlValueTypeWrapper *r = m->as<QmlValueTypeWrapper>(); QV4::ExecutionEngine *v4 = m->engine(); if (!r) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); // Note: readReferenceValue() can change the reference->type. if (r->objectType == QmlValueTypeWrapper::Reference) { @@ -306,7 +306,7 @@ ReturnedValue QmlValueTypeWrapper::get(Managed *m, const StringRef name, bool *h if (result->isFunction()) { // calling a Q_INVOKABLE function of a value type QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); - return QV4::QObjectWrapper::getQmlProperty(v4->current, qmlContext, r->type, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision); + return QV4::QObjectWrapper::getQmlProperty(v4->currentContext(), qmlContext, r->type, name.getPointer(), QV4::QObjectWrapper::IgnoreRevision); } #define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ @@ -339,7 +339,7 @@ void QmlValueTypeWrapper::put(Managed *m, const StringRef name, const ValueRef v Scoped<QmlValueTypeWrapper> r(scope, m->as<QmlValueTypeWrapper>()); if (!r) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return; } @@ -365,7 +365,7 @@ void QmlValueTypeWrapper::put(Managed *m, const StringRef name, const ValueRef v // assigning a JS function to a non-var-property is not allowed. QString error = QLatin1String("Cannot assign JavaScript function to value-type property"); Scoped<String> e(scope, r->v8->toString(error)); - v4->current->throwError(e); + v4->currentContext()->throwError(e); return; } diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 983136a846..ad1e9d862e 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -503,6 +503,12 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type); Q_ASSERT(type.component); + if (profiler.enabled) { + profiler.start(); + profiler.updateTypeName(type.component->name); + profiler.background(); + } + states.push(State()); State *cState = &states[states.count() - 2]; @@ -524,6 +530,11 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QML_END_INSTR(CreateQMLObject) QML_BEGIN_INSTR(CompleteQMLObject) + if (profiler.enabled) { + profiler.foreground(); + profiler.updateLocation(CTXT->url, instr.line, instr.column); + } + QObject *o = objects.top(); Q_ASSERT(o); @@ -566,6 +577,8 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QML_BEGIN_INSTR(CreateCppObject) const QQmlCompiledData::TypeReference &type = TYPES.at(instr.type); Q_ASSERT(type.type); + if (profiler.enabled) + profiler.start(CTXT->url, instr.line, instr.column, type.type->qmlTypeName()); QObject *o = 0; void *memory = 0; @@ -637,12 +650,14 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QML_END_INSTR(CreateCppObject) QML_BEGIN_INSTR(CreateSimpleObject) + const QQmlCompiledData::TypeReference &ref = TYPES.at(instr.type); + if (profiler.enabled) + profiler.start(CTXT->url, instr.line, instr.column, ref.type->qmlTypeName()); QObject *o = (QObject *)operator new(instr.typeSize + sizeof(QQmlData)); ::memset(static_cast<void *>(o), 0, instr.typeSize + sizeof(QQmlData)); instr.create(o); QQmlData *ddata = (QQmlData *)(((const char *)o) + instr.typeSize); - const QQmlCompiledData::TypeReference &ref = TYPES.at(instr.type); if (!ddata->propertyCache && ref.typePropertyCache) { ddata->propertyCache = ref.typePropertyCache; ddata->propertyCache->addref(); @@ -817,6 +832,8 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QML_END_INSTR(StoreScriptString) QML_BEGIN_INSTR(BeginObject) + if (profiler.enabled) + profiler.push(); QObject *target = objects.top(); QQmlParserStatus *status = reinterpret_cast<QQmlParserStatus *>(reinterpret_cast<char *>(target) + instr.castValue); parserStatus.push(status); @@ -1074,6 +1091,8 @@ normalExit: objects.deallocate(); lists.deallocate(); states.clear(); + if (profiler.enabled) + profiler.stop(); return rv; } @@ -1111,6 +1130,8 @@ void QQmlVME::reset() states.clear(); rootContext = 0; creationContext = 0; + if (profiler.enabled) + profiler.clear(); } #ifdef QML_THREADED_VME_INTERPRETER @@ -1170,6 +1191,8 @@ QQmlContextData *QQmlVME::complete(const Interrupt &interrupt) if (componentCompleteEnabled()) { // the qml designer does the component complete later QQmlTrace trace("VME Component Complete"); while (!parserStatus.isEmpty()) { + if (profiler.enabled) + profiler.pop(); QQmlParserStatus *status = parserStatus.pop(); #ifdef QML_ENABLE_TRACE QQmlData *data = parserStatusData.pop(); @@ -1189,6 +1212,8 @@ QQmlContextData *QQmlVME::complete(const Interrupt &interrupt) return 0; } parserStatus.deallocate(); + if (profiler.enabled) + profiler.clear(); } { diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index e76b485a5c..d5afd4c67a 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -68,6 +68,7 @@ #include <private/qfinitestack_p.h> #include <private/qqmltrace_p.h> +#include <private/qqmlprofilerservice_p.h> QT_BEGIN_NAMESPACE @@ -171,6 +172,7 @@ private: #ifdef QML_ENABLE_TRACE QFiniteStack<QQmlData *> parserStatusData; #endif + QQmlVmeProfiler profiler; QQmlGuardedContextData rootContext; QQmlGuardedContextData creationContext; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 4b34792421..ebe72b2ff6 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -956,7 +956,7 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) callData->args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]); QV4::ScopedValue result(scope); - QV4::ExecutionContext *ctx = function->engine()->current; + QV4::ExecutionContext *ctx = function->engine()->currentContext(); result = function->call(callData); if (scope.hasException()) { QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 18e3e33c4b..ad231d0769 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -192,7 +192,7 @@ public: , list(list) , d(data) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); if (d) d->addref(); @@ -226,7 +226,7 @@ public: : Object(engine) , d(data) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); if (d) d->addref(); @@ -258,7 +258,7 @@ public: NodePrototype(ExecutionEngine *engine) : Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope scope(engine); ScopedObject protectThis(scope, this); @@ -312,7 +312,7 @@ class Node : public Object : Object(engine) , d(data) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); if (d) d->addref(); @@ -906,7 +906,7 @@ ReturnedValue NamedNodeMap::getIndexed(Managed *m, uint index, bool *hasProperty QV4::ExecutionEngine *v4 = m->engine(); NamedNodeMap *r = m->as<NamedNodeMap>(); if (!r) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); QV8Engine *engine = v4->v8Engine; @@ -925,7 +925,7 @@ ReturnedValue NamedNodeMap::get(Managed *m, const StringRef name, bool *hasPrope NamedNodeMap *r = m->as<NamedNodeMap>(); QV4::ExecutionEngine *v4 = m->engine(); if (!r) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); name->makeIdentifier(); if (name->equals(v4->id_length)) @@ -961,7 +961,7 @@ ReturnedValue NodeList::getIndexed(Managed *m, uint index, bool *hasProperty) QV4::ExecutionEngine *v4 = m->engine(); NodeList *r = m->as<NodeList>(); if (!r) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); QV8Engine *engine = v4->v8Engine; @@ -980,7 +980,7 @@ ReturnedValue NodeList::get(Managed *m, const StringRef name, bool *hasProperty) QV4::ExecutionEngine *v4 = m->engine(); NodeList *r = m->as<NodeList>(); if (!r) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); name->makeIdentifier(); @@ -1535,7 +1535,7 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const void QQmlXMLHttpRequest::dispatchCallbackImpl(const ValueRef me) { - ExecutionContext *ctx = v4->current; + ExecutionContext *ctx = v4->currentContext(); QV4::Scope scope(v4); Scoped<Object> o(scope, me); if (!o) { @@ -1560,7 +1560,7 @@ void QQmlXMLHttpRequest::dispatchCallbackImpl(const ValueRef me) s = v4->newString(QStringLiteral("ActivationObject")); Scoped<Object> activationObject(scope, o->get(s)); if (!activationObject) { - v4->current->throwError(QStringLiteral("QQmlXMLHttpRequest: internal error: empty ActivationObject")); + v4->currentContext()->throwError(QStringLiteral("QQmlXMLHttpRequest: internal error: empty ActivationObject")); return; } @@ -1580,7 +1580,7 @@ void QQmlXMLHttpRequest::dispatchCallbackImpl(const ValueRef me) void QQmlXMLHttpRequest::dispatchCallback(const ValueRef me) { - ExecutionContext *ctx = v4->current; + ExecutionContext *ctx = v4->currentContext(); dispatchCallbackImpl(me); if (v4->hasException) { QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); @@ -1605,7 +1605,7 @@ struct QQmlXMLHttpRequestWrapper : public Object : Object(engine) , request(request) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } ~QQmlXMLHttpRequestWrapper() { delete request; @@ -1626,7 +1626,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject QQmlXMLHttpRequestCtor(ExecutionEngine *engine) : FunctionObject(engine->rootContext, QStringLiteral("XMLHttpRequest")) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope scope(engine); ScopedValue protectThis(scope, this); @@ -1656,7 +1656,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(that->engine()); Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); if (!ctor) - return that->engine()->current->throwTypeError(); + return that->engine()->currentContext()->throwTypeError(); QV8Engine *engine = that->engine()->v8Engine; QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(engine, engine->networkAccessManager()); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index c80a742af0..41d5de0862 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -90,7 +90,7 @@ QV4::QtObject::QtObject(ExecutionEngine *v4, QQmlEngine *qmlEngine) , m_platform(0) , m_application(0) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); Scope scope(v4); ScopedObject protectThis(scope, this); @@ -1183,7 +1183,7 @@ struct BindingFunction : public QV4::FunctionObject : QV4::FunctionObject(originalFunction->scope, originalFunction->name) , originalFunction(originalFunction) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); bindingKeyFlag = true; } @@ -1608,7 +1608,7 @@ void QV4::GlobalExtensions::init(QQmlEngine *qmlEngine, Object *globalObject) globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); // string prototype extension - v4->stringClass->prototype->defineDefaultProperty(QStringLiteral("arg"), method_string_arg); + v4->stringObjectClass->prototype->defineDefaultProperty(QStringLiteral("arg"), method_string_arg); } @@ -1726,7 +1726,9 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) QString path = ctxt->url.toString(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) : QString(); + int lastDot = path.lastIndexOf(QLatin1Char('.')); + int length = lastDot - (lastSlash + 1); + QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); QString text = ctx->callData->args[0].toQStringNoThrow(); QString comment; diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index d0fc1b1295..33f5a00a6c 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -95,7 +95,7 @@ QV8Engine::QV8Engine(QJSEngine* qq) , m_listModelData(0) { #ifdef Q_PROCESSOR_X86_32 - if (!(qCpuFeatures() & SSE2)) { + if (!qCpuHasFeature(SSE2)) { qFatal("This program requires an X86 processor that supports SSE2 extension, at least a Pentium 4 or newer"); } #endif @@ -256,7 +256,7 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant) case QMetaType::Double: return QV4::Encode(*reinterpret_cast<const double*>(ptr)); case QMetaType::QString: - return m_v4Engine->current->engine->newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue(); + return m_v4Engine->currentContext()->engine->newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(ptr)); case QMetaType::Short: @@ -667,7 +667,7 @@ QV4::ReturnedValue QV8Engine::metaTypeToJS(int type, const void *data) case QMetaType::Double: return QV4::Encode(*reinterpret_cast<const double*>(data)); case QMetaType::QString: - return m_v4Engine->current->engine->newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); + return m_v4Engine->currentContext()->engine->newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(data)); case QMetaType::Short: @@ -750,7 +750,7 @@ bool QV8Engine::metaTypeFromJS(const QV4::ValueRef value, int type, void *data) if (value->isUndefined() || value->isNull()) *reinterpret_cast<QString*>(data) = QString(); else - *reinterpret_cast<QString*>(data) = value->toString(m_v4Engine->current)->toQString(); + *reinterpret_cast<QString*>(data) = value->toString(m_v4Engine->currentContext())->toQString(); return true; case QMetaType::Float: *reinterpret_cast<float*>(data) = value->toNumber(); diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 754d008745..7276c0e5c6 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -71,12 +71,12 @@ struct DelegateModelGroupFunction: QV4::FunctionObject , code(code) , flag(flag) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } static QV4::ReturnedValue construct(QV4::Managed *m, QV4::CallData *) { - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); } static QV4::ReturnedValue call(QV4::Managed *that, QV4::CallData *callData) @@ -86,7 +86,7 @@ struct DelegateModelGroupFunction: QV4::FunctionObject QV4::Scoped<DelegateModelGroupFunction> f(scope, that, QV4::Scoped<DelegateModelGroupFunction>::Cast); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject); if (!o) - return v4->current->throwTypeError(QStringLiteral("Not a valid VisualData object")); + return v4->currentContext()->throwTypeError(QStringLiteral("Not a valid VisualData object")); QV4::ScopedValue v(scope, callData->argument(0)); return f->code(o->item, f->flag, v); @@ -3144,7 +3144,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object QQmlDelegateModelGroupChange(QV4::ExecutionEngine *engine) : Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } static QV4::ReturnedValue method_get_index(QV4::CallContext *ctx) { @@ -3183,7 +3183,7 @@ public: QQmlDelegateModelGroupChangeArray(QV4::ExecutionEngine *engine) : Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); flags &= ~SimpleArray; } virtual ~QQmlDelegateModelGroupChangeArray() {} @@ -3197,7 +3197,7 @@ public: QV4::Scope scope(v4); QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, m->as<QQmlDelegateModelGroupChangeArray>()); if (!array) - return v4->current->throwTypeError(); + return v4->currentContext()->throwTypeError(); if (index >= array->count()) { if (hasProperty) @@ -3221,7 +3221,7 @@ public: { QQmlDelegateModelGroupChangeArray *array = m->as<QQmlDelegateModelGroupChangeArray>(); if (!array) - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); if (name->equals(m->engine()->id_length)) { if (hasProperty) diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 066c8e70e5..f78cf38535 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -165,7 +165,7 @@ struct QQmlDelegateModelItemObject : QV4::Object QQmlDelegateModelItemObject(QV4::ExecutionEngine *engine, QQmlDelegateModelItem *item) : Object(engine) , item(item) - { vtbl = &static_vtbl; } + { setVTable(&static_vtbl); } ~QQmlDelegateModelItemObject(); static void destroy(Managed *that); diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 7607febe01..53e45e2003 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -252,7 +252,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, createsend.value()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::ScopedValue v(scope); QV4::ScopedCallData callData(scope, 1); @@ -356,7 +356,7 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine); QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, workerEngine->onmessage.value()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, workerEngine)); @@ -398,7 +398,7 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) QV4::Script program(v4, activation, sourceCode, url.toString()); - QV4::ExecutionContext *ctx = v4->current; + QV4::ExecutionContext *ctx = v4->currentContext(); program.parse(); if (!v4->hasException) program.run(); diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 9cd5709a49..38fdffdde6 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -902,7 +902,7 @@ void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, list.setList(variant, engine); - if (QObject *object = qvariant_cast<QObject *>(variant)) { + if (QObject *object = qvariant_cast<QObject *>(list.list())) { setObject(object); if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) { accessors = new VDMAbstractItemModelDataType(this); @@ -927,8 +927,8 @@ void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, } else if (list.type() == QQmlListAccessor::ListProperty) { setObject(static_cast<const QQmlListReference *>(variant.constData())->object()); accessors = new VDMObjectDelegateDataType; - } else if (list.type() != QQmlListAccessor::Invalid) { - Q_ASSERT(list.type() != QQmlListAccessor::Instance); // Should have cast to QObject. + } else if (list.type() != QQmlListAccessor::Invalid + && list.type() != QQmlListAccessor::Instance) { // Null QObject setObject(0); accessors = &qt_vdm_list_accessors; } else { |