diff options
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 39 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 45 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 149 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 24 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 11 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 17 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4jsir_p.h | 26 |
9 files changed, 218 insertions, 97 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 03a71768d8..e80b9d8ac4 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1604,7 +1604,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.leaveEnvironment(); scan.leaveEnvironment(); - _env = 0; + _variableEnvironment = 0; _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); for (int i = 0; i < functions.count(); ++i) { @@ -1687,6 +1687,7 @@ enum MetaObjectResolverFlags { }; static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); +static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index); static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver, @@ -1702,6 +1703,14 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, if (ok) { member->setEnumValue(value); return QV4::IR::SInt32Type; + } else { + int index = type->scopedEnumIndex(qmlEngine, *member->name, &ok); + if (ok) { + auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>(); + newResolver->owner = resolver->owner; + initScopedEnumResolver(newResolver, type, index); + return QV4::IR::DiscoveredType(newResolver); + } } } @@ -1885,6 +1894,34 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, resolver->flags = 0; } +static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine, + const QV4::IR::MemberExpressionResolver *resolver, + QV4::IR::Member *member) +{ + if (!member->name->constData()->isUpper()) + return QV4::IR::VarType; + + QQmlType *type = static_cast<QQmlType*>(resolver->data); + int index = resolver->flags; + + bool ok = false; + int value = type->scopedEnumValue(qmlEngine, index, *member->name, &ok); + if (!ok) + return QV4::IR::VarType; + member->setEnumValue(value); + return QV4::IR::SInt32Type; +} + +static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index) +{ + Q_ASSERT(resolver); + + resolver->resolveMember = &resolveScopedEnum; + resolver->data = qmlType; + resolver->extraData = 0; + resolver->flags = index; +} + #endif // V4_BOOTSTRAP void JSCodeGen::beginFunctionBodyHook() diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index d1d22be0ac..c09fde86f1 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -591,21 +591,32 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, if (!string.constData()->isUpper()) return true; + // we support one or two '.' in the enum phrase: + // * <TypeName>.<EnumValue> + // * <TypeName>.<ScopedEnumName>.<EnumValue> + int dot = string.indexOf(QLatin1Char('.')); if (dot == -1 || dot == string.length()-1) return true; - if (string.indexOf(QLatin1Char('.'), dot+1) != -1) - return true; + int dot2 = string.indexOf(QLatin1Char('.'), dot+1); + if (dot2 != -1 && dot2 != string.length()-1) { + if (!string.at(dot+1).isUpper()) + return true; + if (string.indexOf(QLatin1Char('.'), dot2+1) != -1) + return true; + } QHashedStringRef typeName(string.constData(), dot); const bool isQtObject = (typeName == QLatin1String("Qt")); - const QStringRef enumValue = string.midRef(dot + 1); + const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef()); + // ### consider supporting scoped enums in Qt namespace + const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1); - if (isIntProp) { + if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here? // Allow enum assignment to ints. bool ok; - int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok); + int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok); if (ok) { if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject)) return false; @@ -623,18 +634,25 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); if (type && tr && tr->type == type) { + // When these two match, we can short cut the search QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); + QMetaEnum menum = mprop.enumerator(); + QByteArray enumName = enumValue.toUtf8(); + if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8()) + return true; - // When these two match, we can short cut the search if (mprop.isFlagType()) { - value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); + value = menum.keysToValue(enumName.constData(), &ok); } else { - value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); + value = menum.keyToValue(enumName.constData(), &ok); } } else { // Otherwise we have to search the whole type if (type) { - value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); + if (!scopedEnumName.isEmpty()) + value = type->scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok); + else + value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); } else { QByteArray enumName = enumValue.toUtf8(); const QMetaObject *metaObject = StaticQtMetaObject::get(); @@ -651,7 +669,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, return assignEnumToBinding(binding, enumValue, value, isQtObject); } -int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const +int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const { Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer"); *ok = false; @@ -661,13 +679,16 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &e imports->resolveType(scope, &type, 0, 0, 0); if (!type) return -1; - return type->enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok); + if (!enumName.isEmpty()) + return type->scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok); + return type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok); } const QMetaObject *mo = StaticQtMetaObject::get(); int i = mo->enumeratorCount(); + const QByteArray ba = enumValue.toUtf8(); while (i--) { - int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok); + int v = mo->enumerator(i).keyToValue(ba.constData(), ok); if (*ok) return v; } diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 79fc073d8b..76aa422fc5 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -209,7 +209,7 @@ private: bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding); - int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const; + int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const; const QVector<QmlIR::Object*> &qmlObjects; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 1e98d1167b..5b7a7f9050 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -128,7 +128,7 @@ static inline bool isSimpleExpr(IR::Expr *e) Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) - , _env(0) + , _variableEnvironment(0) , _allowFuncDecls(true) , defaultProgramMode(defaultProgramMode) { @@ -142,17 +142,17 @@ void Codegen::ScanFunctions::operator()(Node *node) void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { - Environment *e = _cg->newEnvironment(node, _env, compilationMode); + Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode); if (!e->isStrict) e->isStrict = _cg->_strictMode; _envStack.append(e); - _env = e; + _variableEnvironment = e; } void Codegen::ScanFunctions::leaveEnvironment() { _envStack.pop(); - _env = _envStack.isEmpty() ? 0 : _envStack.top(); + _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top(); } void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) @@ -168,7 +168,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) continue; QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); if (str == QLatin1String("use strict")) { - _env->isStrict = true; + _variableEnvironment->isStrict = true; } else { // TODO: give a warning. } @@ -183,7 +183,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { if (name == QLatin1String("implements") || name == QLatin1String("interface") || name == QLatin1String("let") @@ -201,7 +201,7 @@ void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *paramet { while (parameters) { if (parameters->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; parameters = parameters->next; } } @@ -220,19 +220,19 @@ void Codegen::ScanFunctions::endVisit(Program *) bool Codegen::ScanFunctions::visit(CallExpression *ast) { - if (! _env->hasDirectEval) { + if (! _variableEnvironment->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("eval")) { - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _env->hasDirectEval = true; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _variableEnvironment->hasDirectEval = true; } } } int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -241,7 +241,7 @@ bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -257,26 +257,38 @@ bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index); return true; } bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Environment::Member *m = 0; + if (_variableEnvironment->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope); return true; } bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) { checkName(ast->name, ast->identifierToken); - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; return true; } @@ -308,7 +320,7 @@ bool Codegen::ScanFunctions::visit(FunctionExpression *ast) void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); } @@ -329,7 +341,7 @@ bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) ++argc; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); Node::accept(ast->properties, this); @@ -361,7 +373,7 @@ void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) bool Codegen::ScanFunctions::visit(WithStatement *ast) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); return false; } @@ -371,7 +383,7 @@ bool Codegen::ScanFunctions::visit(WithStatement *ast) bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); } Node::accept(ast->expression, this); @@ -383,7 +395,7 @@ bool Codegen::ScanFunctions::visit(ForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -394,7 +406,7 @@ bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -404,7 +416,7 @@ bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { Node::accept(ast->initialiser, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -414,7 +426,7 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { Node::accept(ast->declaration, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -422,12 +434,12 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { bool Codegen::ScanFunctions::visit(ThisExpression *) { - _env->usesThis = true; + _variableEnvironment->usesThis = true; return false; } bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls); Node::accept(ast->statements, this); return false; } @@ -435,26 +447,26 @@ bool Codegen::ScanFunctions::visit(Block *ast) { void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) { bool wasStrict = false; - if (_env) { - _env->hasNestedFunctions = true; + if (_variableEnvironment) { + _variableEnvironment->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. if (expr) - _env->enter(name, Environment::FunctionDefinition, expr); + _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); if (name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _env->isStrict; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _variableEnvironment->isStrict; } enterEnvironment(ast, FunctionCode); checkForArguments(formals); - _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _env->formals = formals; + _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _variableEnvironment->formals = formals; if (body) checkDirectivePrologue(body->elements); - if (wasStrict || _env->isStrict) { + if (wasStrict || _variableEnvironment->isStrict) { QStringList args; for (FormalParameterList *it = formals; it; it = it->next) { QString arg = it->name.toString(); @@ -478,7 +490,7 @@ Codegen::Codegen(bool strict) , _block(0) , _exitBlock(0) , _returnAddress(0) - , _env(0) + , _variableEnvironment(0) , _loop(0) , _labelledStatement(0) , _scopeAndFinally(0) @@ -498,7 +510,7 @@ void Codegen::generateFromProgram(const QString &fileName, Q_ASSERT(node); _module = module; - _env = 0; + _variableEnvironment = 0; _module->setFileName(fileName); @@ -517,7 +529,7 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, { _module = module; _module->setFileName(fileName); - _env = 0; + _variableEnvironment = 0; ScanFunctions scan(this, sourceCode, GlobalCode); // fake a global environment @@ -534,14 +546,14 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, void Codegen::enterEnvironment(Node *node) { - _env = _envMap.value(node); - Q_ASSERT(_env); + _variableEnvironment = _envMap.value(node); + Q_ASSERT(_variableEnvironment); } void Codegen::leaveEnvironment() { - Q_ASSERT(_env); - _env = _env->parent; + Q_ASSERT(_variableEnvironment); + _variableEnvironment = _variableEnvironment->parent; } void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) @@ -1443,7 +1455,7 @@ bool Codegen::visit(DeleteExpression *ast) return false; // Temporaries cannot be deleted IR::ArgLocal *al = expr->asArgLocal(); - if (al && al->index < static_cast<unsigned>(_env->members.size())) { + if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) { // Trying to delete a function argument might throw. if (_function->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); @@ -1472,7 +1484,7 @@ bool Codegen::visit(DeleteExpression *ast) } if (expr->asTemp() || (expr->asArgLocal() && - expr->asArgLocal()->index >= static_cast<unsigned>(_env->members.size()))) { + expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) { _expr.code = _block->CONST(IR::BoolType, 1); return false; } @@ -1525,7 +1537,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) return 0; uint scope = 0; - Environment *e = _env; + Environment *e = _variableEnvironment; IR::Function *f = _function; while (f && e->parent) { @@ -1556,7 +1568,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) return fallback; - if (!e->parent && (!f || !f->insideWithOrCatch) && _env->compilationMode != EvalCode && e->compilationMode != QmlBinding) + if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding) return _block->GLOBALNAME(name, line, col); // global context or with. Lookup by name @@ -2072,7 +2084,7 @@ bool Codegen::visit(FunctionDeclaration * ast) TempScope scope(_function); - if (_env->compilationMode == QmlBinding) + if (_variableEnvironment->compilationMode == QmlBinding) move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); _expr.accept(nx); return false; @@ -2096,26 +2108,26 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *entryBlock = function->newBasicBlock(0); IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); - function->hasDirectEval = _env->hasDirectEval || _env->compilationMode == EvalCode + function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->usesThis = _env->usesThis; - function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _env->isStrict; - function->isNamedExpression = _env->isNamedFunctionExpression; - function->isQmlBinding = _env->compilationMode == QmlBinding; + function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->usesThis = _variableEnvironment->usesThis; + function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); + function->isStrict = _variableEnvironment->isStrict; + function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression; + function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; function->column = loc.startColumn; if (function->usesArgumentsObject) - _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration); + _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope); // variables in global code are properties of the global context object, not locals as with other functions. - if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) { unsigned t = 0; - for (Environment::MemberMap::iterator it = _env->members.begin(), end = _env->members.end(); it != end; ++it) { + for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) { const QString &local = it.key(); function->LOCAL(local); (*it).index = t; @@ -2123,18 +2135,19 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, ++t; } } else { - if (!_env->isStrict) { + if (!_variableEnvironment->isStrict) { for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, - static_cast<int>(tempIndex), 0 }; - _env->members.insert(inheritedLocal, member); + static_cast<int>(tempIndex), 0, + AST::VariableDeclaration::VariableScope::FunctionScope }; + _variableEnvironment->members.insert(inheritedLocal, member); } } IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _env->members.constBegin(), cend = _env->members.constEnd(); it != cend; ++it) { + for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); IR::ExprList *next = function->New<IR::ExprList>(); next->expr = entryBlock->NAME(local, 0, 0); @@ -2166,11 +2179,11 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - for (const Environment::Member &member : qAsConst(_env->members)) { + for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _env->parent) { + if (! _variableEnvironment->parent) { move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), _block->CLOSURE(function)); } else { @@ -2356,7 +2369,7 @@ bool Codegen::visit(ExpressionStatement *ast) TempScope scope(_function); - if (_env->compilationMode == EvalCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) { Result e = expression(ast->expression); if (*e) move(_block->TEMP(_returnAddress), *e); @@ -2604,7 +2617,7 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_env->compilationMode != FunctionCode && _env->compilationMode != QmlBinding) { + if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } @@ -2992,7 +3005,7 @@ bool Codegen::visit(UiSourceElement *) bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) { - if (!_env->isStrict) + if (!_variableEnvironment->isStrict) return false; if (IR::Name *n = expr->asName()) { if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 1cbe6949a1..31e9cc0f46 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -141,10 +141,14 @@ protected: VariableDeclaration, FunctionDefinition }; + struct Member { MemberType type; int index; AST::FunctionExpression *function; + AST::VariableDeclaration::VariableScope scope; + + bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; } }; typedef QMap<QString, Member> MemberMap; @@ -191,6 +195,18 @@ protected: return (*it).index; } + bool memberInfo(const QString &name, const Member **m) const + { + Q_ASSERT(m); + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) { + *m = 0; + return false; + } + *m = &(*it); + return true; + } + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) { Environment *it = this; @@ -206,7 +222,7 @@ protected: return false; } - void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0) { if (! name.isEmpty()) { if (type != FunctionDefinition) { @@ -220,8 +236,10 @@ protected: m.index = -1; m.type = type; m.function = function; + m.scope = scope; members.insert(name, m); } else { + Q_ASSERT(scope == (*it).scope); if ((*it).type <= type) { (*it).type = type; (*it).function = function; @@ -458,7 +476,7 @@ protected: QV4::IR::BasicBlock *_block; QV4::IR::BasicBlock *_exitBlock; unsigned _returnAddress; - Environment *_env; + Environment *_variableEnvironment; Loop *_loop; AST::LabelledStatement *_labelledStatement; ScopeAndFinally *_scopeAndFinally; @@ -536,7 +554,7 @@ protected: // fields: Codegen *_cg; const QString _sourceCode; - Environment *_env; + Environment *_variableEnvironment; QStack<Environment *> _envStack; bool _allowFuncDecls; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index db707061fe..94abf416a1 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -141,7 +141,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) flags |= IR::RegExp::RegExp_IgnoreCase; if (re->flags & CompiledData::RegExp::RegExp_Multiline) flags |= IR::RegExp::RegExp_Multiline; - runtimeRegularExpressions[i] = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); + QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); + runtimeRegularExpressions[i] = ro; } if (data->lookupTableSize) { @@ -168,8 +169,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->level = -1; l->index = UINT_MAX; l->nameIndex = compiledLookups[i].nameIndex; - if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter) - l->engine = engine; } } @@ -244,14 +243,14 @@ void CompilationUnit::unlink() #endif } -void CompilationUnit::markObjects(QV4::ExecutionEngine *e) +void CompilationUnit::markObjects(QV4::MarkStack *markStack) { for (uint i = 0; i < data->stringTableSize; ++i) if (runtimeStrings[i]) - runtimeStrings[i]->mark(e); + runtimeStrings[i]->mark(markStack); if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(e); + runtimeRegularExpressions[i].mark(markStack); } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 23f5e31ebe..ff0d597b92 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -129,11 +129,12 @@ struct TableIterator struct Location { union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 20> line; QJsonPrivate::qle_bitfield<20, 12> column; }; - Location() { line.val = 0; column.val = 0; } + Location() : _dummy(0) { } inline bool operator<(const Location &other) const { return line < other.line || @@ -149,11 +150,12 @@ struct RegExp RegExp_Multiline = 0x04 }; union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 4> flags; QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - RegExp() { flags.val = 0; stringIndex.val = 0; } + RegExp() : _dummy(0) { } }; struct Lookup @@ -167,21 +169,23 @@ struct Lookup }; union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 4> type_and_flags; QJsonPrivate::qle_bitfield<4, 28> nameIndex; }; - Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } + Lookup() : _dummy(0) { } }; struct JSClassMember { union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 31> nameOffset; QJsonPrivate::qle_bitfield<31, 1> isAccessor; }; - JSClassMember() { nameOffset = 0; isAccessor = 0; } + JSClassMember() : _dummy(0) { } }; struct JSClass @@ -211,7 +215,8 @@ struct Function HasDirectEval = 0x2, UsesArgumentsObject = 0x4, IsNamedExpression = 0x8, - HasCatchOrWith = 0x10 + HasCatchOrWith = 0x10, + CanUseSimpleCall = 0x20 }; // Absolute offset into file where the code for this function is located. Only used when the function @@ -896,7 +901,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); - void markObjects(QV4::ExecutionEngine *e); + void markObjects(MarkStack *markStack); void destroy() Q_DECL_OVERRIDE; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index ede432a1e0..a49388846d 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -296,6 +296,8 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->flags |= CompiledData::Function::IsNamedExpression; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; + if (irFunction->canUseSimpleCall()) + function->flags |= CompiledData::Function::CanUseSimpleCall; function->nFormals = irFunction->formals.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 6f14e3dfaf..190fb1bd5b 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1357,6 +1357,31 @@ struct Function { int getNewStatementId() { return _statementCount++; } int statementCount() const { return _statementCount; } + bool canUseSimpleCall() const { + return nestedFunctions.isEmpty() && + locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount && + !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval; + } + + bool argLocalRequiresWriteBarrier(ArgLocal *al) const { + uint scope = al->scope; + const IR::Function *f = this; + while (scope) { + f = f->outer; + --scope; + } + return !f->canUseSimpleCall(); + } + int localsCountForScope(ArgLocal *al) const { + uint scope = al->scope; + const IR::Function *f = this; + while (scope) { + f = f->outer; + --scope; + } + return f->locals.size(); + } + private: BasicBlock *getOrCreateBasicBlock(int index); void setStatementCount(int cnt); @@ -1425,6 +1450,7 @@ public: ArgLocal *newArgLocal = f->New<ArgLocal>(); newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope); newArgLocal->type = argLocal->type; + newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval; return newArgLocal; } |