On 1/8/16, 7:23 AM, "Andy Dufilie" <andy.dufi...@gmail.com> wrote:
>On Fri, Jan 8, 2016 at 1:43 AM, Alex Harui <aha...@adobe.com> wrote: > >> >> But, IMO, for FlexJS, what really matters is what should happen in the >>JS >> output, and the essence of this thread is that in many cases, we can >> optimize away the call to Language.as. >> > > >> Right now, "function" casting also results in a Language.as call with a >> flag that throws an error if "as" fails. I think we can optimize out a >> lot of these calls as well. > > >Here are some different situations to consider: > >1.) >(x as Foo).bar(a,b); >Foo(x).bar(a,b); >Can optimize either? MAYBE - you MAY get a different error if x is not of >type Foo (possibly "function bar not found on x"). > >2.) >var y:Foo = x as Foo; >var y:Foo = Foo(x); >Can optimize "as"? NO - the code is enforcing x to either be null or an >instance of Foo. The code that follows is not expecting x to be of any >type >besides Foo or null. >Can optimize (cast)? NO - the code is not expecting to continue if x is >not >of type Foo. >If either cast is removed the code becomes incorrect. > >3.) >var y = (x as Foo).property; >var y = Foo(x).property; >Can optimize either? NO - the code should either get the property of an >instance of Foo or throw an error. If "as" is optimized out, you can >either get the property of an unrelated type, or you can get y=undefined. >Either behavior is wrong and could be very hard to track down. The code is >not expecting to continue if x is not of type Foo. > >4.) >(x as Foo).property = value; >Foo(x).property = value; >Can optimize either? NO - the code should either set the property of an >instance of Foo or throw an error. If either cast is removed, you may be >setting the property of an unrelated type which will be very hard to track >down. The code is not expecting to continue if x is not of type Foo. > >We may be able to make the distinction between the first case and the rest >and optimize out the first call to "as", but since the resulting code is >still unpredictable, it should not be the default behavior of the >cross-compiler. > >In all above cases, if Foo is a primitive type, function-style casting can >never be optimized out because Foo(x) will actually change the type of x. >For example, String(x) will convert null to the String "null". Well, IMO, it isn't always NO. Sometimes you can optimize that code away. It isn't "never". Currently, FalconJX does know if you are using function-style casting to primitives and won't optimize that away. And what I'm playing with now for function-style casting won't optimize it away if it finds a try/catch around it. > > > >> In the past, we always generated >> the call and you could suppress it with @flexjsignorecoercion, but I'm >> considering pushing the change where we default to not generating the >>call >> and you have to opt-in with @flexjsemitcoercion. But when you opt-in, >>we >> do want to cause the same behavior that you will get in a SWF. >> >> >A better solution would be to introduce new syntax that always gets >optimized out in both AS+JS and only serves the purpose of "making the >compiler happy". I suggest the following: > >(x:Foo) instead of (x as Foo) > >Examples: >(x:Foo).bar(a,b); >var y:Foo = x:Foo; >var y = (x:Foo).property; >(x:Foo).property = value; >(obj.prop : Foo).bar(); >var y:Number = (a + b):Number; >(obj.prop : pkg.name.Foo).bar(); > >It's easily readable, Action-Scripty, and I believe it can be >distinguished >from all other syntax, including ternary operator, line labels, and >namespacing. >I can work on implementing this feature. You were just complaining about how much code massaging you have to do. We've heard similar feedback from others, so my first instinct is to find a way to give you control over your code output with less massaging. This new syntax would still require massaging. The @flexjsignorecoercion (or now @flexjsemitcoercion) is a function-level directive. We could add project-level, file-level and/or line-level directives if that is easier than massaging the code. New language features may also be a problem for IDE code assist. IMO, an optimizing compiler would be smart enough to know when to remove the Language.as calls. All I'm trying to provide is some smarts in the compiler to do that optimization and some directives to help if we get it wrong. If you still want to add a new language feature, (x:Foo) is valid pattern in a function parameter list, so I don't know if the compiler would give you trouble implementing it. Another option might be another keyword like "as" (maybe "_as_"). > >Maybe someday, we can get the compiler to do >> code-flow analysis and determine if we can optimize out these calls to >> Language.as, but for now, not having these calls makes the code smaller, >> and reduces some of the dependencies/goog.requires which reduces the >> likelihood the Closure Compiler will find a circular dependency. >> >> -Alex >> > >IMO, circular dependencies are developer errors and need not be addressed >by the compiler. They simply cannot be resolved in certain situations. >Ignored, sure, but not necessarily resolved. Circular dependencies are not >specifically related to casting, so I don't think they should be a factor >in this decision. True circular dependencies (that affect initialization) are developer errors. Maybe we are doing it wrong in the compiler today, but the Google Closure Compiler implies that you should have goog.require statements for all dependencies in an output file, and then complains about circular dependencies if you do. Here's the simplest case: --- Parent.as --- class Parent { var children:Vector.<Child>; } --- Child.as --- class Child { var parent:Parent; } FalconJX currently outputs: --- Parent.js --- goog.provide("Parent"); goog.require("Child"); ... --- Child.js --- goog.provide("Child"): goog.require("Parent"); ... Sure, we could force folks to re-code this relationship with interfaces to avoid dependencies, but again, folks migrating large code bases have indicated that they would rather not. FalconJX currently has code that walks the chain of class dependencies from the main class and removes goog.requires that are not required for static initialization of the class. I think I can't know whether to kick out the goog.require in Parent or Child until I see whether the main class referenced Parent or Child first. But it turns out that in all of our FlexJS framework code and examples so far, we don't have any circularities that aren't generated by casting calls, and defaulting to optimizing them away meant that the goog.requires don't get generated and I could save the time of scanning for circularities. I think I will put the circularity scan as a compiler option for folks who still need it. So, I'm leaning toward thinking of this as an "optimizing compiler" problem so folks don't have to touch their existing code as much. Thoughts? -Alex