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

Reply via email to