diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b5b70dbf45..53cbc3c5a0 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -220,7 +220,15 @@ bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); return false; } - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + QString name = ast->name.toString(); + const Environment::Member *m = 0; + if (_env->memberInfo(name, &m)) { + if (m->isConstant || ast->readOnly) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->readOnly); return true; } @@ -391,7 +399,7 @@ void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, Forma _env->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. if (expr) - _env->enter(name, Environment::FunctionDefinition, expr); + _env->enter(name, Environment::FunctionDefinition, false /* readonly */, expr); if (name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; wasStrict = _env->isStrict; @@ -2044,7 +2052,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, function->column = loc.startColumn; if (function->usesArgumentsObject) - _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration); + _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, false /* readonly */); // variables in global code are properties of the global context object, not locals as with other functions. if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { @@ -2062,7 +2070,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, - static_cast(tempIndex), 0 }; + static_cast(tempIndex), 0, false /* readonly */ }; _env->members.insert(inheritedLocal, member); } } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 742ee79648..adfdea8805 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -145,6 +145,7 @@ protected: MemberType type; int index; AST::FunctionExpression *function; + bool isConstant : 1; }; typedef QMap MemberMap; @@ -191,6 +192,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 +219,7 @@ protected: return false; } - void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + void enter(const QString &name, MemberType type, bool isConstant, AST::FunctionExpression *function = 0) { if (! name.isEmpty()) { if (type != FunctionDefinition) { @@ -220,8 +233,10 @@ protected: m.index = -1; m.type = type; m.function = function; + m.isConstant = isConstant; members.insert(name, m); } else { + Q_ASSERT(isConstant == (*it).isConstant); if ((*it).type <= type) { (*it).type = type; (*it).function = function; diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 1f0248c258..53d0912d1c 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -8208,6 +8208,12 @@ void tst_qqmlecmascript::constkw_data() "v + i\n" << false << QVariant(25); + QTest::newRow("const-multiple-scopes-same-var") + << "const v = 3\n" + "function f() { const v = 1; return v; }\n" + "v + f()\n" + << false + << QVariant(4); // error cases QTest::newRow("const-no-initializer") @@ -8218,6 +8224,25 @@ void tst_qqmlecmascript::constkw_data() << "const v = 1, i\n" << true << QVariant("SyntaxError: Missing initializer in const declaration"); + QTest::newRow("const-no-duplicate") + << "const v = 1, v = 2\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("const-no-duplicate-2") + << "const v = 1\n" + "const v = 2\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("const-no-duplicate-var") + << "const v = 1\n" + "var v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); + QTest::newRow("var-no-duplicate-const") + << "var v = 1\n" + "const v = 1\n" + << true + << QVariant("SyntaxError: Identifier v has already been declared"); } void tst_qqmlecmascript::constkw()