Flex:FalconJx
- added 3 tests for 'this' keyword that will produce the 'goog' @this tag for a constructor and method scope

git-svn-id: https://svn.apache.org/repos/asf/incubator/flex/whiteboard@1427004 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java
index 10e2be5..e1e4bcf 100644
--- a/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java
+++ b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java
@@ -188,6 +188,37 @@
         assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n * @override\n */\nA.prototype.foo = function(bar, baz) {\n\tbaz = typeof baz !== 'undefined' ? baz : null;\n\treturn -1;\n}");
     }
 
+    //--------------------------------------------------------------------------
+    // Doc Specific Tests 
+    //--------------------------------------------------------------------------
+    
+    @Test
+    public void testConstructor_withThisInBody()
+    {
+        IFunctionNode node = getMethod("public function A(){this.foo;}");
+        visitor.visitConstructor(node);
+        // TODO Erik; Do we need the @this tag if it's annotated with @constructor?
+        // Right now I have it inserting if 'this' is present in the constructor scope
+        assertOut("/**\n * @constructor\n * @this {A}\n */\nA = function() {\n\tthis.foo;\n}");
+    }
+    
+    @Test
+    public void testMethod_withThisInBody()
+    {
+        IFunctionNode node = getMethod("function foo(){this.foo;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @this {A}\n */\nA.prototype.foo = function() {\n\tthis.foo;\n}");
+    }
+    
+    @Test
+    public void testMethod_withThisInBodyComplex()
+    {
+        IFunctionNode node = getMethod("function foo(){if(true){while(i){this.bar(42);}}}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @this {A}\n */\nA.prototype.foo = function() {\n\tif (true) " +
+        		"{\n\t\twhile (i) {\n\t\t\tthis.bar(42);\n\t\t}\n\t}\n}");
+    }
+    
     @Override
     protected IFunctionNode getMethod(String code)
     {
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java
index 0cb6ba6..5f675a0 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java
@@ -32,11 +32,13 @@
 import org.apache.flex.compiler.definitions.ITypeDefinition;
 import org.apache.flex.compiler.internal.js.codegen.JSEmitter;
 import org.apache.flex.compiler.internal.js.codegen.JSSharedData;
+import org.apache.flex.compiler.internal.semantics.SemanticUtils;
 import org.apache.flex.compiler.internal.tree.as.FunctionNode;
 import org.apache.flex.compiler.js.codegen.goog.IJSGoogDocEmitter;
 import org.apache.flex.compiler.js.codegen.goog.IJSGoogEmitter;
 import org.apache.flex.compiler.problems.ICompilerProblem;
 import org.apache.flex.compiler.projects.ICompilerProject;
+import org.apache.flex.compiler.tree.as.IASNode;
 import org.apache.flex.compiler.tree.as.IClassNode;
 import org.apache.flex.compiler.tree.as.IDefinitionNode;
 import org.apache.flex.compiler.tree.as.IExpressionNode;
@@ -88,6 +90,8 @@
         getDoc().emitConstructor(node);
         if (superClass != null)
             getDoc().emitExtends(superClass);
+        if (containsThisReference(node))
+            getDoc().emitThis(parent);
         getDoc().end();
     }
 
@@ -229,64 +233,60 @@
             }
             else
             {
-            	Boolean hasDoc = false;
-            	
+                boolean hasDoc = false;
+
                 // @this
-                // TODO (erikdebruin) only emit @this when there actually is 
-                //                    a 'this' reference in the method block
-            	/*
-                if (false)
+                if (containsThisReference(node))
                 {
                     getDoc().begin();
-                	hasDoc = true;
-                	
-                	getDoc().emitThis(type);
+                    hasDoc = true;
+
+                    getDoc().emitThis(type);
                 }
-                //*/
-                
+
                 // @param
                 IParameterNode[] parameters = node.getParameterNodes();
                 for (IParameterNode pnode : parameters)
                 {
-                	if (!hasDoc)
-                	{
+                    if (!hasDoc)
+                    {
                         getDoc().begin();
-                    	hasDoc = true;
-                	}
-                		
+                        hasDoc = true;
+                    }
+
                     getDoc().emitParam(pnode);
                 }
-                
+
                 // @return
                 // TODO (erikdebruin) only emit @return when there actually is 
                 //					  a return value defined
                 String returnType = node.getReturnType();
                 if (returnType != "")
                 {
-                	if (!hasDoc)
-                	{
+                    if (!hasDoc)
+                    {
                         getDoc().begin();
-                    	hasDoc = true;
-                	}
-                		
-                	getDoc().emitReturn(node);
+                        hasDoc = true;
+                    }
+
+                    getDoc().emitReturn(node);
                 }
-                
+
                 // @override
                 Boolean override = node.hasModifier(ASModifier.OVERRIDE);
                 if (override)
                 {
-                	if (!hasDoc)
-                	{
+                    if (!hasDoc)
+                    {
                         getDoc().begin();
-                    	hasDoc = true;
-                	}
-                		
-                	getDoc().emitOverride(node);
+                        hasDoc = true;
+                    }
+
+                    getDoc().emitOverride(node);
                 }
-                
+
                 if (hasDoc)
-                	getDoc().end();
+                    getDoc().end();
             }
         }
     }
@@ -295,7 +295,10 @@
     public void emitMethod(IFunctionNode node)
     {
         if (node.isConstructor())
+        {
+            emitConstructor(node);
             return;
+        }
 
         IClassDefinition definition = getClassDefinition(node);
 
@@ -309,7 +312,7 @@
             write(qname);
             write(".");
             if (!fn.hasModifier(ASModifier.STATIC))
-            	write("prototype.");
+                write("prototype.");
         }
 
         emitMemberName(node);
@@ -325,7 +328,7 @@
     public void emitFunctionBlockHeader(IFunctionNode node)
     {
         emitRestParameterCodeBlock(node);
-        
+
         if (JSSharedData.OUTPUT_ALTERNATE)
         {
             emitDefaultParameterCodeBlock_Alternate(node);
@@ -454,8 +457,7 @@
 
             code.append(rest.getName() + " = "
                     + "Array.prototype.slice.call(arguments, "
-                    + (pnodes.length - 1)
-                    + ");\n");
+                    + (pnodes.length - 1) + ");\n");
 
             write(code.toString());
         }
@@ -527,4 +529,24 @@
         IScopedNode scope = node.getScopedNode();
         return scope.getChildCount() > 0;
     }
+
+    private static boolean containsThisReference(IASNode node)
+    {
+        final int len = node.getChildCount();
+        for (int i = 0; i < len; i++)
+        {
+            final IASNode child = node.getChild(i);
+            if (child.getChildCount() > 0)
+            {
+                return containsThisReference(child);
+            }
+            else
+            {
+                if (SemanticUtils.isThisKeyword(child))
+                    return true;
+            }
+        }
+
+        return false;
+    }
 }