Codegen: Disallow duplicate declarations of const properties
Spec 13.3.1.1 (Static Semantics: Early Errors) says: It is a Syntax Error if the BoundNames of BindingList contains any duplicate entries. Only let/const are supposed to be treated in this way, so we ensure that one of them has been marked read-only (since we don't support "let" yet). There's still no runtime check on assigning to a constant-declared variable. [ChangeLog][QtQml] "const" variable declarations now throw a SyntaxError if multiple attempts to declare the same variable name are found. Note that "const" is still not fully spec-compliant (i.e. reassignment at runtime is not disallowed). Task-number: QTBUG-58493 Change-Id: I31fd5f2bf3e79d48734e8ecb714c4e7f47e31d2a Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
parent
b63393c7aa
commit
5f807a6276
|
@ -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<int>(tempIndex), 0 };
|
||||
static_cast<int>(tempIndex), 0, false /* readonly */ };
|
||||
_env->members.insert(inheritedLocal, member);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,7 @@ protected:
|
|||
MemberType type;
|
||||
int index;
|
||||
AST::FunctionExpression *function;
|
||||
bool isConstant : 1;
|
||||
};
|
||||
typedef QMap<QString, Member> 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;
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue