You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by er...@apache.org on 2012/12/30 17:47:03 UTC

svn commit: r1426963 - in /incubator/flex/whiteboard/mschmalle/falconjx: compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/ compiler.jx/src/org/apache/flex/compiler/as/codegen/ compiler.jx/src/org/apache/flex/compiler/internal/as/...

Author: erikdebruin
Date: Sun Dec 30 16:47:02 2012
New Revision: 1426963

URL: http://svn.apache.org/viewvc?rev=1426963&view=rev
Log:
Worked my way through the 'Method Member' tests for the 'goog'-ified JS implementation. In order to get the function block closing semicolon in place, I've 'rerouted' some stuff in the AS and JS emitter base classes. I put comments on the relevant sections, please check my implementation and feel free to give brutal feedback :-)

Added:
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java   (with props)
Modified:
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/codegen/IASEmitter.java
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASAfterNodeStrategy.java
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/JSEmitter.java
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogDocEmitter.java
    incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java

Added: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java?rev=1426963&view=auto
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java (added)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java Sun Dec 30 16:47:02 2012
@@ -0,0 +1,192 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.js.codegen.goog;
+
+import org.apache.flex.compiler.clients.IBackend;
+import org.apache.flex.compiler.internal.as.codegen.TestMethodMembers;
+import org.apache.flex.compiler.internal.js.codegen.JSSharedData;
+import org.apache.flex.compiler.internal.js.driver.goog.GoogBackend;
+import org.apache.flex.compiler.tree.as.IFunctionNode;
+import org.junit.Test;
+
+/**
+ * This class tests the production of 'goog'-ified JS code for Class Method members.
+ * 
+ * @author Michael Schmalle
+ * @author Erik de Bruin
+ */
+public class TestGoogMethodMembers extends TestMethodMembers
+{
+	// TODO (erikdebruin)
+	//  1) ideally '@this' should only be included in the annotation if there is
+	//     actually a reference to 'this' in the function body
+	//  9) switch 'alternate' indicator to point to 'Wienberg style', not mine ;-)
+	// 10) can we safely ignore the 'public' and custom namespaces?
+
+    @Override
+    @Test
+    public void testMethod()
+    {
+        IFunctionNode node = getMethod("function foo(){}");
+        visitor.visitFunction(node);
+        assertOut("A.prototype.foo = function() {\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withReturnType()
+    {
+        IFunctionNode node = getMethod("function foo():int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @return {number}\n */\nA.prototype.foo = function() {\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withParameterReturnType()
+    {
+        IFunctionNode node = getMethod("function foo(bar):int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {*} bar\n * @return {number}\n */\nA.prototype.foo = function(bar) {\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withParameterTypeReturnType()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String):int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string} bar\n * @return {number}\n */\nA.prototype.foo = function(bar) {\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withDefaultParameterTypeReturnType()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String = \"baz\"):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string=} bar\n * @return {number}\n */\nA.prototype.foo = function(bar) {\n\tbar = typeof bar !== 'undefined' ? bar : \"baz\";\n\treturn -1;\n};");
+    }
+
+    @Test
+    public void testMethod_withDefaultParameterTypeReturnType_Alternate()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String = \"baz\"):int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string=} bar\n * @return {number}\n */\nA.prototype.foo = function(bar) {\n\tif (arguments.length < 1) {\n\t\tbar = \"baz\";\n\t}\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withMultipleDefaultParameterTypeReturnType()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String, baz:int = null):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n */\nA.prototype.foo = function(bar, baz) {\n\tbaz = typeof baz !== 'undefined' ? baz : null;\n\treturn -1;\n};");
+    }
+
+    @Test
+    public void testMethod_withMultipleDefaultParameterTypeReturnType_Alternate()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String, baz:int = null):int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n */\nA.prototype.foo = function(bar, baz) {\n\tif (arguments.length < 2) {\n\t\tbaz = null;\n\t}\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withRestParameterTypeReturnType()
+    {
+        IFunctionNode node = getMethod("function foo(bar:String, ...rest):int{\treturn -1;}");
+        visitor.visitFunction(node);
+        assertOut("/**\n * @param {string} bar\n * @param {...} rest\n * @return {number}\n */\nA.prototype.foo = function(bar, rest) {\n\trest = Array.prototype.slice.call(arguments, 1);\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withNamespace()
+    {
+        IFunctionNode node = getMethod("public function foo(bar:String, baz:int = null):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        // we ignore the 'public' namespace completely
+        assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n */\nA.prototype.foo = function(bar, baz) {\n\tbaz = typeof baz !== 'undefined' ? baz : null;\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withNamespaceCustom()
+    {
+        IFunctionNode node = getMethod("mx_internal function foo(bar:String, baz:int = null):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        // we ignore the custom namespaces completely (are there side effects I'm missing?)
+        assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n */\nA.prototype.foo = function(bar, baz) {\n\tbaz = typeof baz !== 'undefined' ? baz : null;\n\treturn -1;\n};");
+    }
+    
+    @Override
+    @Test
+    public void testMethod_withNamespaceModifiers()
+    {
+        IFunctionNode node = getMethod("public static function foo(bar:String, baz:int = null):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        // (erikdebruin) here we actually DO want to declare the method
+        //               directly on the 'class' constructor instead of the
+        //               prototype!
+        assertOut("/**\n * @param {string} bar\n * @param {number=} baz\n * @return {number}\n */\nA.foo = function(bar, baz) {\n\tbaz = typeof baz !== 'undefined' ? baz : null;\n\treturn -1;\n};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withNamespaceModifierOverride()
+    {
+        IFunctionNode node = getMethod("public override function foo(bar:String, baz:int = null):int{\treturn -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        assertOutDebug("/**\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};");
+    }
+
+    @Override
+    @Test
+    public void testMethod_withNamespaceModifierOverrideBackwards()
+    {
+        IFunctionNode node = getMethod("override public function foo(bar:String, baz:int = null):int{return -1;}");
+    	JSSharedData.OUTPUT_ALTERNATE = true;
+        visitor.visitFunction(node);
+        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};");
+    }
+
+    @Override
+    protected IFunctionNode getMethod(String code)
+    {
+    	JSSharedData.OUTPUT_ALTERNATE = false;
+    	
+    	return super.getMethod(code);
+    }
+
+    @Override
+    protected IBackend createBackend()
+    {
+        return new GoogBackend();
+    }
+}

Propchange: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/compiler/internal/js/codegen/goog/TestGoogMethodMembers.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/codegen/IASEmitter.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/codegen/IASEmitter.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/codegen/IASEmitter.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/codegen/IASEmitter.java Sun Dec 30 16:47:02 2012
@@ -62,6 +62,11 @@ public interface IASEmitter
     void indentPush();
 
     /**
+     * Writes the block closing character(s)
+     */
+    void writeBlockClose();
+
+    /**
      * Pops an indent from the emitter so after newlines are emitted, the output
      * is correctly formatted.
      */

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASAfterNodeStrategy.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASAfterNodeStrategy.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASAfterNodeStrategy.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASAfterNodeStrategy.java Sun Dec 30 16:47:02 2012
@@ -60,7 +60,9 @@ public class ASAfterNodeStrategy impleme
                     emitter.indentPop();
                     emitter.write("\n");
                 }
-                emitter.write("}");
+                
+                // (erikdebruin) moved this to utility method to allow overriding
+                emitter.writeBlockClose();
             }
             else if (type == ContainerType.IMPLICIT
                     || type == ContainerType.SYNTHESIZED)

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java Sun Dec 30 16:47:02 2012
@@ -147,6 +147,16 @@ public class ASEmitter implements IASEmi
         currentIndent--;
     }
 
+    // (erikdebruin) I needed a way to add a semi-colon after the closing curly
+    //               bracket of a block in the 'goog'-ified output. Instead of 
+    // 				 subclassing 'ASAfterNodeStrategy' and  copying
+    //               the entire function body, I thought I might use this little
+    //               utility method and override that. Am I doing it right?
+    public void writeBlockClose()
+    {
+        write("}");
+    }
+
     public void writeIndent()
     {
         String indent = "";

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/JSEmitter.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/JSEmitter.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/JSEmitter.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/JSEmitter.java Sun Dec 30 16:47:02 2012
@@ -117,4 +117,11 @@ public class JSEmitter extends ASEmitter
         //        }
     }
 
+    @Override
+    public void writeBlockClose()
+    {
+    	super.writeBlockClose();
+        
+    	write(";");
+    }
 }

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogDocEmitter.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogDocEmitter.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogDocEmitter.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogDocEmitter.java Sun Dec 30 16:47:02 2012
@@ -91,15 +91,21 @@ public class JSGoogDocEmitter extends JS
     @Override
     public void emitOverride(IFunctionNode node)
     {
-        // TODO Auto-generated method stub
-
+        write(" * @override\n");
     }
 
     @Override
     public void emitParam(IParameterNode node)
     {
-        write(" * @param {" + node.getVariableType() + "} " + node.getName()
-                + "\n");
+    	String postfix = (node.getDefaultValue() == null) ? "" : "=";
+    	
+    	String paramType = "";
+    	if (node.isRest())
+    		paramType = "...";
+    	else
+    		paramType = convertASTypeToJS(node.getVariableType());
+    	
+        write(" * @param {" + paramType + postfix + "} " + node.getName() + "\n");
     }
 
     @Override
@@ -122,7 +128,7 @@ public class JSGoogDocEmitter extends JS
         // TODO convert js types
         String rtype = node.getReturnType();
         if (rtype != null)
-            write(" * @return {" + rtype + "}\n");
+            write(" * @return {" + convertASTypeToJS(rtype) + "}\n");
     }
 
     @Override
@@ -135,8 +141,9 @@ public class JSGoogDocEmitter extends JS
     public void emitType(IASNode node)
     {
         //String type = SemanticUtils.getTypeOfStem(node, emitter.getProject());
-        String type = ((IVariableNode) node).getVariableType(); // XXX need to map to js types
-        write(" * @type {" + type + "}\n");
+        String type = ((IVariableNode) node).getVariableType(); 
+        // XXX need to map to js types
+        write(" * @type {" + convertASTypeToJS(type) + "}\n");
     }
 
     @Override
@@ -155,4 +162,22 @@ public class JSGoogDocEmitter extends JS
         end();
     }
 
+    //--------------------------------------------------------------------------
+
+    private String convertASTypeToJS(String name)
+    {
+    	String result = name;
+    	
+    	if (name.equals(""))
+    		result = "*";
+    	
+    	if (name.equals("String"))
+    		result = "string";
+    	
+    	if (name.equals("int") || name.equals("uint"))
+    		result = "number";
+    	
+        return result;
+    }
+
 }

Modified: incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java
URL: http://svn.apache.org/viewvc/incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java?rev=1426963&r1=1426962&r2=1426963&view=diff
==============================================================================
--- incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java (original)
+++ incubator/flex/whiteboard/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codegen/goog/JSGoogEmitter.java Sun Dec 30 16:47:02 2012
@@ -26,6 +26,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.flex.compiler.common.ASModifier;
 import org.apache.flex.compiler.definitions.IClassDefinition;
 import org.apache.flex.compiler.definitions.IPackageDefinition;
 import org.apache.flex.compiler.definitions.ITypeDefinition;
@@ -228,17 +229,64 @@ public class JSGoogEmitter extends JSEmi
             }
             else
             {
-                getDoc().begin();
-                getDoc().emitThis(type);
+            	Boolean hasDoc = false;
+            	
+                // @this
+                // TODO (erikdebruin) only emit @this when there actually is 
+                //                    a 'this' reference in the method block
+            	/*
+                if (false)
+                {
+                    getDoc().begin();
+                	hasDoc = true;
+                	
+                	getDoc().emitThis(type);
+                }
+                //*/
+                
                 // @param
                 IParameterNode[] parameters = node.getParameterNodes();
                 for (IParameterNode pnode : parameters)
                 {
+                	if (!hasDoc)
+                	{
+                        getDoc().begin();
+                    	hasDoc = true;
+                	}
+                		
                     getDoc().emitParam(pnode);
                 }
+                
                 // @return
-                getDoc().emitReturn(node);
-                getDoc().end();
+                // TODO (erikdebruin) only emit @return when there actually is 
+                //					  a return value defined
+                String returnType = node.getReturnType();
+                if (returnType != "")
+                {
+                	if (!hasDoc)
+                	{
+                        getDoc().begin();
+                    	hasDoc = true;
+                	}
+                		
+                	getDoc().emitReturn(node);
+                }
+                
+                // @override
+                Boolean override = node.hasModifier(ASModifier.OVERRIDE);
+                if (override)
+                {
+                	if (!hasDoc)
+                	{
+                        getDoc().begin();
+                    	hasDoc = true;
+                	}
+                		
+                	getDoc().emitOverride(node);
+                }
+                
+                if (hasDoc)
+                	getDoc().end();
             }
         }
     }
@@ -260,6 +308,8 @@ public class JSGoogEmitter extends JSEmi
         {
             write(qname);
             write(".");
+            if (!fn.hasModifier(ASModifier.STATIC))
+            	write("prototype.");
         }
 
         emitMemberName(node);
@@ -274,6 +324,8 @@ public class JSGoogEmitter extends JSEmi
     @Override
     public void emitFunctionBlockHeader(IFunctionNode node)
     {
+        emitRestParameterCodeBlock(node);
+        
         if (JSSharedData.OUTPUT_ALTERNATE)
         {
             emitDefaultParameterCodeBlock_Alternate(node);
@@ -391,6 +443,24 @@ public class JSGoogEmitter extends JSEmi
         }
     }
 
+    private void emitRestParameterCodeBlock(IFunctionNode node)
+    {
+        IParameterNode[] pnodes = node.getParameterNodes();
+
+        IParameterNode rest = getRest(pnodes);
+        if (rest != null)
+        {
+            final StringBuilder code = new StringBuilder();
+
+            code.append(rest.getName() + " = "
+                    + "Array.prototype.slice.call(arguments, "
+                    + (pnodes.length - 1)
+                    + ");\n");
+
+            write(code.toString());
+        }
+    }
+
     @Override
     public void emitParameter(IParameterNode node)
     {
@@ -428,6 +498,17 @@ public class JSGoogEmitter extends JSEmi
         return result;
     }
 
+    private IParameterNode getRest(IParameterNode[] nodes)
+    {
+        for (IParameterNode node : nodes)
+        {
+            if (node.isRest())
+                return node;
+        }
+
+        return null;
+    }
+
     private static ITypeDefinition getTypeDefinition(IDefinitionNode node)
     {
         ITypeNode tnode = (ITypeNode) node.getAncestorOfType(ITypeNode.class);