first attempt at Proxy support
Project: http://git-wip-us.apache.org/repos/asf/flex-falcon/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-falcon/commit/c71abdbf Tree: http://git-wip-us.apache.org/repos/asf/flex-falcon/tree/c71abdbf Diff: http://git-wip-us.apache.org/repos/asf/flex-falcon/diff/c71abdbf Branch: refs/heads/master Commit: c71abdbfa5f23c4ecfa751b860554cefeeaf591f Parents: f05767e Author: Alex Harui <[email protected]> Authored: Fri Feb 19 06:57:14 2016 -0800 Committer: Alex Harui <[email protected]> Committed: Fri Feb 19 06:57:14 2016 -0800 ---------------------------------------------------------------------- .../js/flexjs/TestFlexJSGlobalClasses.java | 64 ++++++++++++++++++++ .../flex/compiler/internal/test/TestBase.java | 3 + .../codegen/js/flexjs/JSFlexJSEmitter.java | 51 ++++++++++++++++ .../codegen/js/jx/BinaryOperatorEmitter.java | 38 ++++++++++++ .../internal/codegen/js/jx/ForEachEmitter.java | 17 ++++++ .../codegen/js/jx/MemberAccessEmitter.java | 33 ++++++++++ .../flex/compiler/config/Configuration.java | 46 ++++++++++++-- .../compiler/internal/projects/FlexProject.java | 17 ++++++ .../projects/FlexProjectConfigurator.java | 2 + .../internal/tree/as/IdentifierNode.java | 4 +- 10 files changed, 268 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx.tests/src/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSGlobalClasses.java ---------------------------------------------------------------------- diff --git a/compiler.jx.tests/src/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSGlobalClasses.java b/compiler.jx.tests/src/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSGlobalClasses.java index 547a9ed..b67ded1 100644 --- a/compiler.jx.tests/src/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSGlobalClasses.java +++ b/compiler.jx.tests/src/org/apache/flex/compiler/internal/codegen/js/flexjs/TestFlexJSGlobalClasses.java @@ -27,6 +27,7 @@ import org.apache.flex.compiler.internal.projects.FlexJSProject; import org.apache.flex.compiler.internal.tree.as.VariableNode; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IBinaryOperatorNode; +import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.IForLoopNode; import org.apache.flex.compiler.tree.as.IFunctionNode; import org.apache.flex.compiler.tree.as.IUnaryOperatorNode; @@ -44,6 +45,7 @@ public class TestFlexJSGlobalClasses extends TestGoogGlobalClasses { project = new FlexJSProject(workspace); ((FlexJSProject)project).config = new JSGoogConfiguration(); + project.setProxyBaseClass("flash.utils.Proxy"); super.setUp(); } @@ -469,4 +471,66 @@ public class TestFlexJSGlobalClasses extends TestGoogGlobalClasses asBlockWalker.visitVariable(node); assertOut("var /** @type {Array} */ a = new Array(['Hello', 'World'])"); } + + + @Test + public void testProxy() + { + IFunctionNode node = (IFunctionNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();a.foo = 'bar'; }}", + IFunctionNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @export\n */\nfoo.bar.B.prototype.b = function() {\n var /** @type {flash.utils.Proxy} */ a = new flash.utils.Proxy();\n a.setProperty('foo', 'bar');\n}"); + } + + @Test + public void testProxyGet() + { + IFunctionNode node = (IFunctionNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();var bar:* = a.foo; }}", + IFunctionNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @export\n */\nfoo.bar.B.prototype.b = function() {\n var /** @type {flash.utils.Proxy} */ a = new flash.utils.Proxy();\n var /** @type {*} */ bar = a.getProperty('foo');\n}"); + } + + @Test + public void testProxyConcat() + { + IFunctionNode node = (IFunctionNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();var baz:String = a.foo + 'bar'; }}", + IFunctionNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @export\n */\nfoo.bar.B.prototype.b = function() {\n var /** @type {flash.utils.Proxy} */ a = new flash.utils.Proxy();\n var /** @type {string} */ baz = a.getProperty('foo') + 'bar';\n}"); + } + + @Test + public void testProxyAddAndAssign() + { + IFunctionNode node = (IFunctionNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();a.foo += 'bar'; }}", + IFunctionNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @export\n */\nfoo.bar.B.prototype.b = function() {\n var /** @type {flash.utils.Proxy} */ a = new flash.utils.Proxy();\n a.setProperty('foo', a.getProperty('foo') + 'bar');\n}"); + } + + @Test + public void testProxyForLoop() + { + IForLoopNode node = (IForLoopNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();for (var p:* in a) delete a[p];; }}", + IForLoopNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitForLoop(node); + assertOut("for (var /** @type {*} */ p in a.propertyNames())\n a.deleteProperty(p);"); + } + + @Test + public void testProxyForEachLoop() + { + IForLoopNode node = (IForLoopNode) getNode( + "import flash.utils.Proxy; public class B {public function b() { var a:Proxy = new Proxy();for each (var p:String in a) var i:int = p.length; }}", + IForLoopNode.class, WRAP_LEVEL_PACKAGE, true); + asBlockWalker.visitForLoop(node); + assertOut("var foreachiter0_target = a;\nfor (var foreachiter0 in foreachiter0_target.propertyNames()) \n{\nvar p = foreachiter0_target.getProperty(foreachiter0);\n\n var /** @type {number} */ i = p.length;}\n"); + } + } http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx.tests/src/org/apache/flex/compiler/internal/test/TestBase.java ---------------------------------------------------------------------- diff --git a/compiler.jx.tests/src/org/apache/flex/compiler/internal/test/TestBase.java b/compiler.jx.tests/src/org/apache/flex/compiler/internal/test/TestBase.java index 7b83e79..edaa29f 100644 --- a/compiler.jx.tests/src/org/apache/flex/compiler/internal/test/TestBase.java +++ b/compiler.jx.tests/src/org/apache/flex/compiler/internal/test/TestBase.java @@ -110,7 +110,10 @@ public class TestBase implements ITestBase errors = new ArrayList<ICompilerProblem>(); if (project == null) + { project = new FlexProject(workspace); + project.setProxyBaseClass("flash.utils.Proxy"); + } project.setProblems(errors); FlexProjectConfigurator.configure(project); http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitter.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitter.java b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitter.java index 6fb1f11..70dbb58 100644 --- a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitter.java +++ b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/flexjs/JSFlexJSEmitter.java @@ -28,6 +28,7 @@ import org.apache.flex.compiler.codegen.js.goog.IJSGoogDocEmitter; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IPackageDefinition; +import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitter; import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; @@ -667,6 +668,21 @@ public class JSFlexJSEmitter extends JSGoogEmitter implements IJSFlexJSEmitter write(ASEmitterTokens.PAREN_CLOSE); return; } + else if (isProxy((IdentifierNode)(node.getChild(0).getChild(0)))) + { + if (ASNodeUtils.hasParenOpen(node)) + write(ASEmitterTokens.PAREN_OPEN); + + getWalker().walk(node.getChild(0).getChild(0)); + DynamicAccessNode dan = (DynamicAccessNode)(node.getChild(0)); + IASNode indexNode = dan.getChild(1); + write(".deleteProperty("); + getWalker().walk(indexNode); + write(")"); + if (ASNodeUtils.hasParenClose(node)) + write(ASEmitterTokens.PAREN_CLOSE); + return; + } } } @@ -758,6 +774,41 @@ public class JSFlexJSEmitter extends JSGoogEmitter implements IJSFlexJSEmitter * @param obj * @return */ + public boolean isProxy(IExpressionNode obj) + { + FlexJSProject project = (FlexJSProject)getWalker().getProject(); + // See if it is Proxy + ITypeDefinition leftDef = obj.resolveType(project); + if (leftDef == null) + { + if (obj.getNodeID() == ASTNodeID.MemberAccessExpressionID) + { + IExpressionNode leftNode = ((MemberAccessExpressionNode)obj).getLeftOperandNode(); + leftDef = leftNode.resolveType(project); + if (leftDef != null && leftDef.isInstanceOf(project.getProxyBaseClass(), project)) + return true; + while (leftNode.getNodeID() == ASTNodeID.MemberAccessExpressionID) + { + // walk up chain looking for a proxy + leftNode = ((MemberAccessExpressionNode)obj).getLeftOperandNode(); + leftDef = leftNode.resolveType(project); + if (leftDef != null && leftDef.isInstanceOf(project.getProxyBaseClass(), project)) + return true; + } + } + return false; + } + return leftDef.isInstanceOf(project.getProxyBaseClass(), project); + } + + /** + * resolveType on an XML expression returns null + * (see IdentiferNode.resolveType). + * So, we have to walk the tree ourselves and resolve + * individual pieces. + * @param obj + * @return + */ public boolean isXML(IExpressionNode obj) { // See if the left side is XML or XMLList http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java index 5b200de..03a0f2c 100644 --- a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java +++ b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java @@ -184,6 +184,39 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements return; } } + else if (((JSFlexJSEmitter)getEmitter()).isProxy((MemberAccessExpressionNode)leftSide)) + { + MemberAccessExpressionNode proxyNode = (MemberAccessExpressionNode)leftSide; + if (node.getNodeID() == ASTNodeID.Op_AssignId) + { + getWalker().walk(proxyNode.getLeftOperandNode()); + IExpressionNode rightSide = proxyNode.getRightOperandNode(); + write(".setProperty('"); + getWalker().walk(rightSide); + write("', "); + getWalker().walk(node.getRightOperandNode()); + write(ASEmitterTokens.PAREN_CLOSE); + return; + } + else if (node.getNodeID() == ASTNodeID.Op_AddAssignID) + { + IExpressionNode rightSide = proxyNode.getRightOperandNode(); + getWalker().walk(proxyNode.getLeftOperandNode()); + write(".setProperty('"); + getWalker().walk(rightSide); + write("', "); + getWalker().walk(proxyNode.getLeftOperandNode()); + write(".getProperty("); + write(ASEmitterTokens.SINGLE_QUOTE); + getWalker().walk(rightSide); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + write(" + "); + getWalker().walk(node.getRightOperandNode()); + write(ASEmitterTokens.PAREN_CLOSE); + return; + } + } } super_emitBinaryOperator(node); @@ -325,6 +358,11 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements { write(".elementNames()"); } + else if (node.getNodeID() == ASTNodeID.Op_InID && + ((JSFlexJSEmitter)getEmitter()).isProxy(node.getRightOperandNode())) + { + write(".propertyNames()"); + } } if (ASNodeUtils.hasParenOpen(node)) http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/ForEachEmitter.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/ForEachEmitter.java b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/ForEachEmitter.java index 62382fe..501f5ae 100644 --- a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/ForEachEmitter.java +++ b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/ForEachEmitter.java @@ -86,6 +86,7 @@ public class ForEachEmitter extends JSSubEmitter implements write(ASEmitterTokens.SPACE); write(targetName); boolean isXML = false; + boolean isProxy = false; if (obj.getNodeID() == ASTNodeID.IdentifierID) { if (((JSFlexJSEmitter)getEmitter()).isXML((IdentifierNode)obj)) @@ -93,6 +94,11 @@ public class ForEachEmitter extends JSSubEmitter implements write(".elementNames()"); isXML = true; } + if (((JSFlexJSEmitter)getEmitter()).isProxy((IdentifierNode)obj)) + { + write(".propertyNames()"); + isProxy = true; + } } else if (obj.getNodeID() == ASTNodeID.MemberAccessExpressionID) { @@ -101,6 +107,11 @@ public class ForEachEmitter extends JSSubEmitter implements write(".elementNames()"); isXML = true; } + if (((JSFlexJSEmitter)getEmitter()).isProxy((MemberAccessExpressionNode)obj)) + { + write(".propertyNames()"); + isXML = true; + } } writeToken(ASEmitterTokens.PAREN_CLOSE); writeNewline(); @@ -124,6 +135,12 @@ public class ForEachEmitter extends JSSubEmitter implements write(iterName); write(")"); } + else if (isProxy) + { + write(".getProperty("); + write(iterName); + write(")"); + } else { write(ASEmitterTokens.SQUARE_OPEN); http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/MemberAccessEmitter.java ---------------------------------------------------------------------- diff --git a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/MemberAccessEmitter.java b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/MemberAccessEmitter.java index 2f5aa55..518def2 100644 --- a/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/MemberAccessEmitter.java +++ b/compiler.jx/src/org/apache/flex/compiler/internal/codegen/js/jx/MemberAccessEmitter.java @@ -70,10 +70,15 @@ public class MemberAccessEmitter extends JSSubEmitter implements // could be XML JSFlexJSEmitter fjs = (JSFlexJSEmitter)getEmitter(); boolean isXML = false; + boolean isProxy = false; if (leftNode instanceof MemberAccessExpressionNode) isXML = fjs.isXMLList((MemberAccessExpressionNode)leftNode); else if (leftNode instanceof IExpressionNode) isXML = fjs.isXML((IExpressionNode)leftNode); + if (leftNode instanceof MemberAccessExpressionNode) + isProxy = fjs.isProxy((MemberAccessExpressionNode)leftNode); + else if (leftNode instanceof IExpressionNode) + isProxy = fjs.isProxy((IExpressionNode)leftNode); if (isXML) { boolean descendant = (node.getOperator() == OperatorType.DESCENDANT_ACCESS); @@ -105,6 +110,34 @@ public class MemberAccessEmitter extends JSSubEmitter implements return; } } + else if (isProxy) + { + boolean child = (node.getOperator() == OperatorType.MEMBER_ACCESS) && + (!(parentNode instanceof FunctionCallNode)) && + rightNode.getNodeID() != ASTNodeID.Op_AtID; + if (child) + { + writeLeftSide(node, leftNode, rightNode); + if (child) + write(".getProperty('"); + String s = fjs.stringifyNode(rightNode); + int dot = s.indexOf('.'); + if (dot != -1) + { + String name = s.substring(0, dot); + String afterDot = s.substring(dot); + write(name); + write("')"); + write(afterDot); + } + else + { + write(s); + write("')"); + } + return; + } + } } boolean isStatic = false; if (def != null && def.isStatic()) http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler/src/org/apache/flex/compiler/config/Configuration.java ---------------------------------------------------------------------- diff --git a/compiler/src/org/apache/flex/compiler/config/Configuration.java b/compiler/src/org/apache/flex/compiler/config/Configuration.java index 35d2c4a..79c98be 100644 --- a/compiler/src/org/apache/flex/compiler/config/Configuration.java +++ b/compiler/src/org/apache/flex/compiler/config/Configuration.java @@ -1720,6 +1720,26 @@ public class Configuration } // + // 'compiler.proxy-base-class' option + // + + private String proxyBaseClass = "org.apache.flex.utils.Proxy"; + + public String getProxyBaseClass() + { + return proxyBaseClass; + } + + /** + * The class for proxy code generation + */ + @Config(advanced = true) + public void setCompilerProxyBaseClass(ConfigurationValue cv, String b) + { + proxyBaseClass = b; + } + + // // 'compiler.component-factory-class' option // @@ -3952,19 +3972,35 @@ public class Configuration { try { - File f = new File("unittest.properties"); + File f = new File("../env.properties"); in = new FileInputStream(f); properties = new Properties(); properties.load(in); in.close(); - properties.setProperty("env.PLAYERGLOBAL_HOME", properties.getProperty("PLAYERGLOBAL_HOME")); - properties.setProperty("env.AIR_HOME", properties.getProperty("AIR_HOME")); - properties.setProperty("env.PLAYERGLOBAL_VERSION", properties.getProperty("PLAYERGLOBAL_VERSION")); return properties; } catch (FileNotFoundException e) { - return null; + try + { + File f = new File("unittest.properties"); + in = new FileInputStream(f); + properties = new Properties(); + properties.load(in); + in.close(); + properties.setProperty("env.PLAYERGLOBAL_HOME", properties.getProperty("PLAYERGLOBAL_HOME")); + properties.setProperty("env.AIR_HOME", properties.getProperty("AIR_HOME")); + properties.setProperty("env.PLAYERGLOBAL_VERSION", properties.getProperty("PLAYERGLOBAL_VERSION")); + return properties; + } + catch (FileNotFoundException e1) + { + return null; + } + catch (IOException e1) + { + return null; + } } catch (IOException e) { http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler/src/org/apache/flex/compiler/internal/projects/FlexProject.java ---------------------------------------------------------------------- diff --git a/compiler/src/org/apache/flex/compiler/internal/projects/FlexProject.java b/compiler/src/org/apache/flex/compiler/internal/projects/FlexProject.java index 5c98e5e..574285d 100644 --- a/compiler/src/org/apache/flex/compiler/internal/projects/FlexProject.java +++ b/compiler/src/org/apache/flex/compiler/internal/projects/FlexProject.java @@ -255,6 +255,13 @@ public class FlexProject extends ASProject implements IFlexProject private String factoryInterface; /** + * The fully-qualified name of the proxy base class + * that causes the compiler to generate getProperty/setProperty calls. + * Currently this is "flash.utils.Proxy". + */ + private String proxyBaseClass; + + /** * The fully-qualified name of the runtime class * that creates an IFactory from a class. * Currently this is "mx.core.ClassFactory". @@ -741,6 +748,16 @@ public class FlexProject extends ASProject implements IFlexProject this.mxmlObjectInterface = mxmlObjectInterface; } + public String getProxyBaseClass() + { + return proxyBaseClass; + } + + public void setProxyBaseClass(String proxyBaseClass) + { + this.proxyBaseClass = proxyBaseClass; + } + public String getClassFactoryClass() { return classFactoryClass; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler/src/org/apache/flex/compiler/internal/projects/FlexProjectConfigurator.java ---------------------------------------------------------------------- diff --git a/compiler/src/org/apache/flex/compiler/internal/projects/FlexProjectConfigurator.java b/compiler/src/org/apache/flex/compiler/internal/projects/FlexProjectConfigurator.java index 13b1fd4..04f385b 100644 --- a/compiler/src/org/apache/flex/compiler/internal/projects/FlexProjectConfigurator.java +++ b/compiler/src/org/apache/flex/compiler/internal/projects/FlexProjectConfigurator.java @@ -240,6 +240,8 @@ public class FlexProjectConfigurator configValue = configuration.getComponentFactoryClass(); project.setClassFactoryClass(configValue); + configValue = configuration.getProxyBaseClass(); + project.setProxyBaseClass(configValue); } } } http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c71abdbf/compiler/src/org/apache/flex/compiler/internal/tree/as/IdentifierNode.java ---------------------------------------------------------------------- diff --git a/compiler/src/org/apache/flex/compiler/internal/tree/as/IdentifierNode.java b/compiler/src/org/apache/flex/compiler/internal/tree/as/IdentifierNode.java index c47a48a..1d18923 100644 --- a/compiler/src/org/apache/flex/compiler/internal/tree/as/IdentifierNode.java +++ b/compiler/src/org/apache/flex/compiler/internal/tree/as/IdentifierNode.java @@ -842,8 +842,8 @@ public class IdentifierNode extends ExpressionNodeBase implements IIdentifierNod // and x is type XML you would get a can't-convert-Object-to-String // problem, but there is lots of existing source code that expects // this to compile with no cast. - if (isXMLish(baseType, project)) - return null; + //if (isXMLish(baseType, project)) + // return null; if (baseExpr instanceof IdentifierNode) {
