ensure correct initialization order for local variables
authorLars Knoll <lars.knoll@digia.com>
Mon, 17 Dec 2012 20:56:19 +0000 (21:56 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Tue, 18 Dec 2012 07:19:36 +0000 (08:19 +0100)
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>
qv4codegen.cpp
qv4codegen_p.h

index a15ba22..0e5eedb 100644 (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);
     }
 
index 2cb3235..132bfcc 100644 (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;
+                    }
                 }
             }
         }