From ce99372c63eb5738460f11600fdc619ab7b7eadb Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Tue, 20 Apr 2010 15:19:37 +0200
Subject: [PATCH] QmlJS: Add variables and functions in a JS function to code
 model.

Allows completion of local variable and function names.

Task-number: QTCREATORBUG-942
Reviewed-by: Roberto Raggi
---
 src/libs/qmljs/qmljsbind.cpp         | 39 +++++++++++++++++++++++++---
 src/libs/qmljs/qmljsbind.h           |  3 +++
 src/libs/qmljs/qmljsscopebuilder.cpp |  8 ++----
 3 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp
index daa047c50e6..dba6cbe0aca 100644
--- a/src/libs/qmljs/qmljsbind.cpp
+++ b/src/libs/qmljs/qmljsbind.cpp
@@ -107,6 +107,11 @@ bool Bind::usesQmlPrototype(ObjectValue *prototype,
     return false;
 }
 
+Interpreter::ObjectValue *Bind::findFunctionScope(AST::FunctionDeclaration *node) const
+{
+    return _functionScopes.value(node);
+}
+
 ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue)
 {
     ObjectValue *oldObjectValue = _currentObjectValue;
@@ -289,9 +294,37 @@ bool Bind::visit(FunctionDeclaration *ast)
     //    return false;
 
     ASTFunctionValue *function = new ASTFunctionValue(ast, &_engine);
-    // ### set the function's scope.
-
     _currentObjectValue->setProperty(ast->name->asString(), function);
 
-    return false; // ### eventually want to visit function bodies
+    // build function scope
+    ObjectValue *functionScope = _engine.newObject(/*prototype=*/0);
+    _functionScopes.insert(ast, functionScope);
+    ObjectValue *parent = switchObjectValue(functionScope);
+
+    // The order of the following is important. Example: A function with name "arguments"
+    // overrides the arguments object, a variable doesn't.
+
+    // 1. Function formal arguments
+    for (FormalParameterList *it = ast->formals; it; it = it->next) {
+        if (it->name)
+            functionScope->setProperty(it->name->asString(), _engine.undefinedValue());
+    }
+
+    // 2. Functions defined inside the function body
+    // ### TODO, currently covered by the accept(body)
+
+    // 3. Arguments object
+    ObjectValue *arguments = _engine.newObject(/*prototype=*/0);
+    arguments->setProperty(QLatin1String("callee"), function);
+    arguments->setProperty(QLatin1String("length"), _engine.numberValue());
+    functionScope->setProperty(QLatin1String("arguments"), arguments);
+
+    // 4. Variables defined inside the function body
+    // ### TODO, currently covered by the accept(body)
+
+    // visit body
+    accept(ast->body);
+    switchObjectValue(parent);
+
+    return false;
 }
diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h
index 82ed248ce53..a74d8c92ccd 100644
--- a/src/libs/qmljs/qmljsbind.h
+++ b/src/libs/qmljs/qmljsbind.h
@@ -63,6 +63,8 @@ public:
     bool usesQmlPrototype(Interpreter::ObjectValue *prototype,
                           Interpreter::Context *context) const;
 
+    Interpreter::ObjectValue *findFunctionScope(AST::FunctionDeclaration *node) const;
+
     static QString toString(AST::UiQualifiedId *qualifiedId, QChar delimiter = QChar('.'));
 
 protected:
@@ -101,6 +103,7 @@ private:
     Interpreter::ObjectValue *_rootObjectValue;
 
     QHash<AST::Node *, Interpreter::ObjectValue *> _qmlObjects;
+    QHash<AST::FunctionDeclaration *, Interpreter::ObjectValue *> _functionScopes;
     QStringList _includedScripts;
 
     QStringList _fileImports;
diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp
index 9c63ebbc3a1..a8b84fc5500 100644
--- a/src/libs/qmljs/qmljsscopebuilder.cpp
+++ b/src/libs/qmljs/qmljsscopebuilder.cpp
@@ -32,12 +32,8 @@ void ScopeBuilder::push(AST::Node *node)
 
     // JS scopes
     if (FunctionDeclaration *fun = cast<FunctionDeclaration *>(node)) {
-        ObjectValue *activation = _context->engine()->newObject(/*prototype = */ 0);
-        for (FormalParameterList *it = fun->formals; it; it = it->next) {
-            if (it->name)
-                activation->setProperty(it->name->asString(), _context->engine()->undefinedValue());
-        }
-        _context->scopeChain().jsScopes += activation;
+        ObjectValue *functionScope = _doc->bind()->findFunctionScope(fun);
+        _context->scopeChain().jsScopes += functionScope;
     }
 
     _context->scopeChain().update();
-- 
GitLab