ensure correct initialization order for local variables

section 10.5 requires that function definitions get initialized
at the beginning of the method. variable declarations do not
override the function definitions. assignments to variables
happen when they appear in the source code.

Also remove a duplicated intializations of variables to
undefined. This is already being done by initCallContext
or builtin_declare_vars, so no need to do it in the generated
code again.

Change-Id: I63805b97017f8676d57e0662073689e852b6ac23
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2012-12-17 21:56:19 +01:00 committed by Simon Hausmann
parent 2522d2d3ff
commit 97625f03a6
2 changed files with 70 additions and 41 deletions

View File

@ -308,7 +308,7 @@ protected:
checkName(ast->name, ast->identifierToken);
if (ast->name == QLatin1String("arguments"))
_env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
_env->enter(ast->name.toString());
_env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration);
return true;
}
@ -324,7 +324,7 @@ protected:
{
if (_env) {
_env->hasNestedFunctions = true;
_env->enter(ast->name.toString());
_env->enter(ast->name.toString(), Environment::FunctionDefinition);
}
enterEnvironment(ast);
checkForArguments(ast->formals);
@ -340,9 +340,8 @@ protected:
virtual bool visit(FunctionDeclaration *ast)
{
_env->functions.append(ast);
_env->hasNestedFunctions = true;
_env->enter(ast->name.toString());
_env->enter(ast->name.toString(), Environment::FunctionDefinition, ast);
if (ast->name == QLatin1String("arguments"))
_env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
enterEnvironment(ast);
@ -771,14 +770,11 @@ void Codegen::sourceElements(SourceElements *ast)
void Codegen::variableDeclaration(VariableDeclaration *ast)
{
IR::Expr *initializer = 0;
if (ast->expression) {
Result expr = expression(ast->expression);
assert(expr.code);
initializer = *expr;
}
if (! initializer)
initializer = _block->CONST(IR::UndefinedType, 0);
if (!ast->expression)
return;
Result expr = expression(ast->expression);
assert(expr.code);
initializer = *expr;
if (! _env->parent || _function->insideWith) {
// it's global code.
@ -1250,7 +1246,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col)
}
}
if (index >= _env->vars.size()) {
if (index >= _env->members.size()) {
// named local variable, e.g. in a catch statement
return _block->TEMP(index);
}
@ -1695,16 +1691,16 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
// variables in global code are properties of the global context object, not locals as with other functions.
if (_mode == FunctionCode) {
for (int i = 0; i < _env->vars.size(); ++i) {
const QString &local = _env->vars.at(i);
for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) {
const QString &local = it.key();
function->LOCAL(local);
unsigned t = entryBlock->newTemp();
assert(t == unsigned(i));
(*it).index = t;
}
} else {
IR::ExprList *args = 0;
for (int i = 0; i < _env->vars.size(); ++i) {
const QString &local = _env->vars.at(i);
for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) {
const QString &local = it.key();
IR::ExprList *next = function->New<IR::ExprList>();
next->expr = entryBlock->NAME(local, 0, 0);
next->next = args;
@ -1739,17 +1735,20 @@ IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast,
_function->RECEIVE(it->name.toString());
}
foreach (AST::FunctionDeclaration *f, _env->functions) {
IR::Function *function = defineFunction(f->name.toString(), f, f->formals,
f->body ? f->body->elements : 0);
if (_debugger)
_debugger->setSourceLocation(function, f->functionToken.startLine, f->functionToken.startColumn);
if (! _env->parent)
move(_block->NAME(f->name.toString(), f->identifierToken.startLine, f->identifierToken.startColumn),
_block->CLOSURE(function));
else
move(_block->TEMP(_env->findMember(f->name.toString())),
_block->CLOSURE(function));
foreach (const Environment::Member &member, _env->members) {
if (member.function) {
IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
member.function->body ? member.function->body->elements : 0);
if (_debugger)
_debugger->setSourceLocation(function, member.function->functionToken.startLine, member.function->functionToken.startColumn);
if (! _env->parent) {
move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
_block->CLOSURE(function));
} else {
assert(member.index >= 0);
move(_block->TEMP(member.index), _block->CLOSURE(function));
}
}
}
sourceElements(body);
@ -2209,13 +2208,18 @@ bool Codegen::visit(TryStatement *ast)
// the variable used in the catch statement is local and hides any global
// variable with the same name.
int hiddenIndex = _env->findMember(ast->catchExpression->name.toString());
_env->members.insert(ast->catchExpression->name.toString(), exception);
const Environment::Member undefinedMember = { Environment::UndefinedMember, -1 , 0 };
const Environment::Member catchMember = { Environment::VariableDefinition, exception, 0 };
Environment::Member m = _env->members.value(ast->catchExpression->name.toString(), undefinedMember);
_env->members.insert(ast->catchExpression->name.toString(), catchMember);
statement(ast->catchExpression->statement);
// reset the variable name to the one from the outer scope
_env->members.insert(ast->catchExpression->name.toString(), hiddenIndex);
if (m.type == Environment::UndefinedMember)
_env->members.remove(ast->catchExpression->name.toString());
else
_env->members.insert(ast->catchExpression->name.toString(), m);
_block->JUMP(finallyBody);
}

View File

@ -43,6 +43,7 @@
#include "qv4ir_p.h"
#include <private/qqmljsastvisitor_p.h>
#include <assert.h>
namespace QQmlJS {
@ -110,9 +111,21 @@ protected:
struct Environment {
Environment *parent;
QHash<QString, int> members;
QVector<QString> vars;
QVector<AST::FunctionDeclaration *> functions;
enum MemberType {
UndefinedMember,
VariableDefinition,
VariableDeclaration,
FunctionDefinition
};
struct Member {
MemberType type;
int index;
AST::FunctionDeclaration *function;
};
typedef QMap<QString, Member> MemberMap;
MemberMap members;
int maxNumberOfArguments;
bool hasDirectEval;
bool hasNestedFunctions;
@ -139,7 +152,11 @@ protected:
int findMember(const QString &name) const
{
return members.value(name, -1);
MemberMap::const_iterator it = members.find(name);
if (it == members.end())
return -1;
assert((*it).index != -1 || !parent);
return (*it).index;
}
bool lookupMember(const QString &name, Environment **scope, int *index, int *distance)
@ -157,13 +174,21 @@ protected:
return false;
}
void enter(const QString &name)
void enter(const QString &name, MemberType type, AST::FunctionDeclaration *function = 0)
{
if (! name.isEmpty()) {
int idx = members.value(name, -1);
if (idx == -1) {
members.insert(name, vars.count());
vars.append(name);
MemberMap::iterator it = members.find(name);
if (it == members.end()) {
Member m;
m.index = -1;
m.type = type;
m.function = function;
members.insert(name, m);
} else {
if ((*it).type < type) {
(*it).type = type;
(*it).function = function;
}
}
}
}