Flex:FalconJx
- First implementation of an extended emitter JSGoogEmitter.java
- The compiler is spitting out semi-valid goog format

git-svn-id: https://svn.apache.org/repos/asf/incubator/flex/whiteboard@1425206 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/driver/TestWalkerBase.java b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/driver/TestWalkerBase.java
index 42971ed..1a21a31 100644
--- a/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/driver/TestWalkerBase.java
+++ b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/driver/TestWalkerBase.java
@@ -3,9 +3,6 @@
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertThat;
 
-import java.io.StringWriter;
-import java.io.Writer;
-
 import org.apache.flex.compiler.clients.IBackend;
 import org.apache.flex.compiler.internal.as.codegen.ASFilterWriter;
 import org.apache.flex.compiler.internal.driver.ASBackend;
@@ -16,8 +13,6 @@
 {
     protected IASBlockVisitor visitor;
 
-    private Writer out;
-
     private IBackend backend;
 
     private ASFilterWriter writer;
@@ -29,9 +24,8 @@
     {
         super.setUp();
 
-        backend = new ASBackend();
-        out = new StringWriter();
-        writer = new ASFilterWriter(out);
+        backend = createBackend();
+        writer = backend.createFilterWriter(project);
         visitor = backend.createWalker(project, errors, writer);
     }
 
@@ -39,14 +33,18 @@
     public void tearDown()
     {
         backend = null;
-        out = null;
         writer = null;
         visitor = null;
     }
 
+    protected IBackend createBackend()
+    {
+        return new ASBackend();
+    }
+
     protected void assertOut(String code)
     {
-        mCode = out.toString();
-        assertThat(out.toString(), is(code));
+        mCode = writer.toString();
+        assertThat(writer.toString(), is(code));
     }
 }
diff --git a/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/js/codegen/TestGoogEmiter.java b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/js/codegen/TestGoogEmiter.java
new file mode 100644
index 0000000..3c5f68b
--- /dev/null
+++ b/mschmalle/falconjx/compiler.jx.tests/src/org/apache/flex/js/internal/js/codegen/TestGoogEmiter.java
@@ -0,0 +1,75 @@
+/*
+ *
+ *  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.js.internal.js.codegen;
+
+import java.io.FilterWriter;
+
+import org.apache.flex.compiler.clients.IBackend;
+import org.apache.flex.compiler.internal.driver.JSBackend;
+import org.apache.flex.compiler.internal.js.codgen.JSEmitter;
+import org.apache.flex.compiler.internal.js.codgen.JSGoogEmitter;
+import org.apache.flex.compiler.tree.as.IFileNode;
+import org.apache.flex.js.internal.driver.TestWalkerBase;
+import org.junit.Test;
+
+/**
+ * This class tests the production of 'goog' JavaScript output.
+ * <p>
+ * Note; this is a complete prototype more used in figuring out where
+ * abstraction and indirection is needed concerning the AS -> JS translations.
+ * 
+ * @author Michael Schmalle
+ */
+public class TestGoogEmiter extends TestWalkerBase
+{
+    // emitPackageHeader()
+    // emitImports()
+    // emitClass()
+
+    @Test
+    public void testSimple()
+    {
+        String code = "package com.example.components {"
+                + "import org.apache.flex.html.staticControls.TextButton;"
+                + "public class MyTextButton extends TextButton {"
+                + "public function MyTextButton() {if (foo() != 42) { bar(); } }"
+                + "private var _privateVar:String = \"do \";"
+                + "public var publicProperty:Number = 100;"
+                + "public function myFunction(value: String): String{"
+                + "return \"Don't \" + _privateVar + value; }";
+        IFileNode node = getFileNode(code);
+        visitor.visitFile(node);
+        //assertOut("");
+    }
+
+    protected IBackend createBackend()
+    {
+        return new GoogBackend();
+    }
+
+    class GoogBackend extends JSBackend
+    {
+        @Override
+        protected JSEmitter createEmitter(FilterWriter out)
+        {
+            return new JSGoogEmitter(out);
+        }
+    }
+}
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/IASEmitter.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/IASEmitter.java
index 634fe19..9bf3141 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/IASEmitter.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/as/IASEmitter.java
@@ -25,6 +25,7 @@
 import org.apache.flex.compiler.tree.as.IExpressionNode;
 import org.apache.flex.compiler.tree.as.IFunctionNode;
 import org.apache.flex.compiler.tree.as.IGetterNode;
+import org.apache.flex.compiler.tree.as.IPackageNode;
 import org.apache.flex.compiler.tree.as.ISetterNode;
 import org.apache.flex.compiler.tree.as.IVariableNode;
 import org.apache.flex.compiler.visitor.IASBlockWalker;
@@ -61,6 +62,14 @@
      */
     void indentPop();
 
+    void emitPackageHeader(IPackageNode node);
+
+    void emitPackageHeaderContents(IPackageNode node);
+
+    void emitPackageContents(IPackageNode node);
+
+    void emitPackageFooter(IPackageNode node);
+
     /**
      * Emit a documentation comment for a Class field or constant
      * {@link IVariableNode}.
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java
index a894135..ee85969 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/as/codegen/ASEmitter.java
@@ -42,9 +42,11 @@
 import org.apache.flex.compiler.tree.as.IFunctionNode;
 import org.apache.flex.compiler.tree.as.IGetterNode;
 import org.apache.flex.compiler.tree.as.IKeywordNode;
+import org.apache.flex.compiler.tree.as.IPackageNode;
 import org.apache.flex.compiler.tree.as.IParameterNode;
 import org.apache.flex.compiler.tree.as.IScopedNode;
 import org.apache.flex.compiler.tree.as.ISetterNode;
+import org.apache.flex.compiler.tree.as.ITypeNode;
 import org.apache.flex.compiler.tree.as.IVariableNode;
 import org.apache.flex.compiler.visitor.IASBlockWalker;
 
@@ -139,6 +141,51 @@
     }
 
     //--------------------------------------------------------------------------
+    // IPackageNode
+    //--------------------------------------------------------------------------
+
+    @Override
+    public void emitPackageHeader(IPackageNode node)
+    {
+        write("package");
+
+        String name = node.getQualifiedName();
+        if (name != null && !name.equals(""))
+        {
+            write(" ");
+            getWalker().walk(node.getNameExpressionNode());
+        }
+
+        write(" ");
+        write("{");
+    }
+
+    @Override
+    public void emitPackageHeaderContents(IPackageNode node)
+    {
+    }
+
+    @Override
+    public void emitPackageContents(IPackageNode node)
+    {
+        ITypeNode tnode = findTypeNode(node);
+        if (tnode != null)
+        {
+            indentPush();
+            write("\n");
+            getWalker().walk(tnode); // IClassNode | IInterfaceNode
+        }
+    }
+
+    @Override
+    public void emitPackageFooter(IPackageNode node)
+    {
+        indentPop();
+        write("\n");
+        write("}");
+    }
+
+    //--------------------------------------------------------------------------
     // 
     //--------------------------------------------------------------------------
 
@@ -434,4 +481,30 @@
         // TODO (mschmalle) FunctionObjectNode; does this need specific treatment?
         emitMethodScope(node);
     }
+
+    protected ITypeNode findTypeNode(IPackageNode node)
+    {
+        IScopedNode scope = node.getScopedNode();
+        for (int i = 0; i < scope.getChildCount(); i++)
+        {
+            IASNode child = scope.getChild(i);
+            if (child instanceof ITypeNode)
+                return (ITypeNode) child;
+        }
+        return null;
+    }
+    
+    protected static IFunctionNode getConstructor(IDefinitionNode[] members)
+    {
+        for (IDefinitionNode node : members)
+        {
+            if (node instanceof IFunctionNode)
+            {
+                IFunctionNode fnode = (IFunctionNode) node;
+                if (fnode.isConstructor())
+                    return fnode;
+            }
+        }
+        return null;
+    }
 }
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/driver/JSBackend.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/driver/JSBackend.java
index 7a768c8..023596f 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/driver/JSBackend.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/driver/JSBackend.java
@@ -19,6 +19,7 @@
 
 package org.apache.flex.compiler.internal.driver;
 
+import java.io.FilterWriter;
 import java.io.StringWriter;
 import java.util.List;
 
@@ -73,8 +74,8 @@
     }
 
     @Override
-    public JSTarget createTarget(IASProject project,
-            ITargetSettings settings, ITargetProgressMonitor monitor)
+    public JSTarget createTarget(IASProject project, ITargetSettings settings,
+            ITargetProgressMonitor monitor)
     {
         return new JSTarget(project, settings, monitor);
     }
@@ -83,7 +84,7 @@
     public IASBlockWalker createWalker(IASProject project,
             List<ICompilerProblem> errors, ASFilterWriter out)
     {
-        JSEmitter emitter = new JSEmitter(out);
+        JSEmitter emitter = createEmitter(out);
         ASBlockWalker walker = new ASBlockWalker(errors, project, emitter);
 
         BeforeAfterStrategy strategy = new BeforeAfterStrategy(
@@ -111,4 +112,9 @@
         return new JSWriter(project, problems, compilationUnit, enableDebug);
     }
 
+    protected JSEmitter createEmitter(FilterWriter out)
+    {
+        return new JSEmitter(out);
+    }
+
 }
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/ASBlockWalker.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/ASBlockWalker.java
index 0caa59b..e4546e0 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/ASBlockWalker.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/ASBlockWalker.java
@@ -258,29 +258,11 @@
     public void visitPackage(IPackageNode node)
     {
         debug("visitPackage()");
-        emitter.write("package");
 
-        String name = node.getQualifiedName();
-        if (name != null && !name.equals(""))
-        {
-            emitter.write(" ");
-            walk(node.getNameExpressionNode());
-        }
-
-        emitter.write(" ");
-        emitter.write("{");
-
-        ITypeNode tnode = findTypeNode(node);
-        if (tnode != null)
-        {
-            emitter.indentPush();
-            emitter.write("\n");
-            walk(tnode); // IClassNode | IInterfaceNode
-        }
-
-        emitter.indentPop();
-        emitter.write("\n");
-        emitter.write("}");
+        emitter.emitPackageHeader(node);
+        emitter.emitPackageHeaderContents(node);
+        emitter.emitPackageContents(node);
+        emitter.emitPackageFooter(node);
     }
 
     @Override
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSEmitter.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSEmitter.java
index 5d18c55..41022fe 100644
--- a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSEmitter.java
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSEmitter.java
@@ -54,12 +54,6 @@
         jsdoc.emmitPackageHeader(node);
     }
 
-    public void emitProvide(ITypeDefinition definition)
-    {
-        write("goog.provide('" + definition.getQualifiedName() + "');");
-        writeNewline();
-    }
-
     public void emitRequire(ITypeDefinition definition)
     {
         IPackageDefinition parent = (IPackageDefinition) definition.getParent();
diff --git a/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSGoogEmitter.java b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSGoogEmitter.java
new file mode 100644
index 0000000..d87164d
--- /dev/null
+++ b/mschmalle/falconjx/compiler.jx/src/org/apache/flex/compiler/internal/js/codgen/JSGoogEmitter.java
@@ -0,0 +1,161 @@
+/*
+ *
+ *  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.codgen;
+
+import java.io.FilterWriter;
+import java.util.ArrayList;
+
+import org.apache.flex.compiler.definitions.IClassDefinition;
+import org.apache.flex.compiler.definitions.IPackageDefinition;
+import org.apache.flex.compiler.internal.tree.as.FunctionNode;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.compiler.tree.as.IClassNode;
+import org.apache.flex.compiler.tree.as.IDefinitionNode;
+import org.apache.flex.compiler.tree.as.IExpressionNode;
+import org.apache.flex.compiler.tree.as.IFunctionNode;
+import org.apache.flex.compiler.tree.as.IPackageNode;
+import org.apache.flex.compiler.tree.as.ITypeNode;
+import org.apache.flex.compiler.tree.as.IVariableNode;
+
+public class JSGoogEmitter extends JSEmitter
+{
+    private IClassDefinition classDefinition;
+
+    @Override
+    public void emitPackageHeader(IPackageNode node)
+    {
+        ITypeNode type = findTypeNode(node);
+        write("goog.provide('" + type.getQualifiedName() + "');");
+        write("\n");
+        write("\n");
+    }
+
+    @Override
+    public void emitPackageHeaderContents(IPackageNode node)
+    {
+        IPackageDefinition parent = (IPackageDefinition) node.getDefinition();
+        ArrayList<String> list = new ArrayList<String>();
+        parent.getContainedScope().getScopeNode().getAllImports(list);
+        for (String imp : list)
+        {
+            if (imp.indexOf("__AS3__") != -1)
+                continue;
+            write("goog.require('" + imp + "');");
+            write("\n");
+        }
+        write("\n");
+    }
+
+    @Override
+    public void emitPackageContents(IPackageNode node)
+    {
+        ITypeNode type = findTypeNode(node);
+        IClassNode cnode = (IClassNode) type;
+        classDefinition = cnode.getDefinition();
+        // constructor
+        emitConstructor((IFunctionNode) classDefinition.getConstructor()
+                .getNode());
+
+        IDefinitionNode[] members = cnode.getAllMemberNodes();
+        for (IDefinitionNode dnode : members)
+        {
+            if (dnode instanceof IVariableNode)
+            {
+                emitField((IVariableNode) dnode);
+            }
+        }
+
+        for (IDefinitionNode dnode : members)
+        {
+            if (dnode instanceof IFunctionNode)
+            {
+                emitMethod((IFunctionNode) dnode);
+            }
+        }
+    }
+
+    @Override
+    public void emitPackageFooter(IPackageNode node)
+    {
+    }
+
+    @Override
+    public void emitConstructor(IFunctionNode node)
+    {
+        FunctionNode fn = (FunctionNode) node;
+        fn.parseFunctionBody(new ArrayList<ICompilerProblem>());
+        
+        emitJSDocConstructor(node, getWalker().getProject());
+        
+        String qname = classDefinition.getQualifiedName();
+        write(qname);
+        write(" ");
+        write("=");
+        write(" ");
+        write("function");
+        emitParamters(node.getParameterNodes());
+        emitMethodScope(node.getScopedNode());
+        write("\n");
+        write("\n");
+    }
+
+    public void emitField(IVariableNode node)
+    {
+        emitJSDocVariable(node);
+        write(classDefinition.getQualifiedName() + ".prototype."
+                + node.getName());
+        IExpressionNode vnode = node.getAssignedValueNode();
+        if (vnode != null)
+        {
+            write(" = ");
+            getWalker().walk(vnode);
+        }
+        write(";\n");
+        write("\n");
+    }
+
+    @Override
+    public void emitMethod(IFunctionNode node)
+    {
+        if (node.isConstructor())
+            return;
+        
+        emitJSDoc(node, getWalker().getProject(), false, classDefinition);
+        FunctionNode fn = (FunctionNode) node;
+        fn.parseFunctionBody(new ArrayList<ICompilerProblem>());
+
+        String qname = classDefinition.getQualifiedName();
+        write(qname);
+        write(" ");
+        write("=");
+        write(" ");
+        write("function");
+        emitParamters(node.getParameterNodes());
+        emitMethodScope(node.getScopedNode());
+        write("\n");
+        write("\n");
+    }
+
+    public JSGoogEmitter(FilterWriter out)
+    {
+        super(out);
+    }
+
+}