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:
parent
2522d2d3ff
commit
97625f03a6
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue