Test Available In FalconJx (+/-)?

ActionScript

JavaScript + Closure annotation

Notes

Global Constants

 

 

 

 

Infinity

+

Infinity

Infinity

 

-Infinity

+

-Infinity

-Infinity

 

NaN

+

NaN

NaN

 

undefined

+

undefined

undefined

 

 

 

 

 

 

Global Functions

 

 

 

 

Array()

+

Array(...values)

Array(...values)

 

Boolean()

+

Boolean(value)

Boolean(value)

 

decodeURI()

+

decodeURI(value)

decodeURI(value)

 

decodeURIComponent()

+

decodeURIComponent(value)

decodeURIComponent(value)

 

encodeURI()

+

encodeURI(value)

encodeURI(value)

 

encodeURIComponent()

+

encodeURIComponent(value)

encodeURIComponent(value)

 

escape()

+

escape(value)

escape(value)

 

int()

+

int(value)

int(value)

See "diff" page...

isFinite()

+

isFinite(value)

isFinite(value)

 

isNaN()

+

isNaN(value)

isNaN(value)

 

isXMLName()

+

isXMLName(value)

See "E4X" page...

 

Number()

+

Number(value)

Number(value)

 

Object()

+

Object(value)

Object(value)

 

parseFloat()

+

parseFloat(value)

parseFloat(value)

 

parseInt()

+

parseInt(value, [radix])

parseInt(value, [radix])

 

String()

+

String(value)

String(value)

 

trace()

+

trace(...values)

trace(...values)

See "diff" page...

uint()

+

uint(value)

uint(value)

See "diff" page...

unescape()

+

unescape(value)

unescape(value)

 

Vector()

+

result = Vector.<type>(valueArray);

See "diff" page...

 

XML()

+

XML(value)

See "E4X" page...

 

XMLList()

+

XMLList(value)

See "E4X" page...


 

 

 

 

 

Classes

 

 

 

 

ArgumentError

+

ArgumentError

ArgumentError

See "diff" page...

arguments

+

arguments

arguments

 

Array

+

Array

Array

See "diff" page...

Boolean

+

Boolean

Boolean

 

Class

+

-

-

See "Class" page...

Date

+

Date

Date

See "diff" page...

DefinitionError

+

DefinitionError

DefinitionError

See "diff" page...

Error

+

Error

Error

See "diff" page...

EvalError

+

EvalError

EvalError

See "diff" page...

Function

+

Function

Function

 

int

+

int

int

See "diff" page...

JSON

+

JSON

JSON

See "diff" page...

Math

+

Math

Math

 

Namespace

+

Namespace

Namespace

See "diff" page...

Number

+

Number

Number

 

Object

+

Object

Object

See "diff" page...

QName

+

QName

See "E4X" page...

 

RangeError

+

RangeError

RangeError

See "diff" page...

ReferenceError

+

ReferenceError

ReferenceError

See "diff" page...

RegExp

+

RegExp

RegExp

See "diff" page...

SecurityError

+

SecurityError

SecurityError

See "diff" page...

String

+

String

String

See "diff" page...

SyntaxError

+

SyntaxError

SyntaxError

See "diff" page...

TypeError

+

TypeError

TypeError

See "diff" page...

uint

+

uint

uint

See "diff" page...

URIError

+

URIError

URIError

See "diff" page...

Vector

+

Vector

See "diff" page...

 

VerifyError

+

VerifyError

VerifyError

See "diff" page...

XML

+

XML

See "E4X" page...

 

XMLList

+

XMLList

See "E4X" page...


 

 

 

 

 

Arithmetic

 

 

 

 

+

+

+

+

 

--

+

--

--

 

/

+

/

/

 

++

+

++

++

 

%

+

%

%

 

*

+

*

*

 

-

+

-

-

 

 

 

 

 

 

Arithmetic compound assignment

 

 

 

 

+=

+

+=

+=

 

/=

+

/=

/=

 

%=

+

%=

%=

 

*=

+

*=

*=

 

-=

+

-=

-=

 

 

 

 

 

 

Assignment

 

 

 

 

=

+

=

=

 

 

 

 

 

 

Bitwise

 

 

 

 

&

+

&

&

 

<<

+

<<

<<

 

~

+

~

~

 

|

+

|

|

 

>>

+

>>

>>

 

>>>

+

>>>

>>>

 

^

+

^

^

 

 

 

 

 

 

Bitwise compound assignment

 

 

 

 

&=

+

&=

&=

 

<<=

+

<<=

<<=

 

|=

+

|=

|=

 

>>=

+

>>=

>>=

 

>>>=

+

>>>=

>>>=

 

^=

+

^=

^=

 

 

 

 

 

 

Comment

 

 

 

 

/**/

+

/**/

/**/

 

//

+

//

//

 

 

 

 

 

 

Comparison

 

 

 

 

==

+

==

==

 

>

+

>

>

 

>=

+

>=

>=

 

!=

+

!=

!=

 

<

+

<

<

 

<=

+

<=

<=

 

===

+

===

===

 

!==

+

!==

!==

 

 

 

 

 

 

Logical

 

 

 

 

&&

+

&&

&&

 

&&=

+

x &&= y

See "diff" page...

 

!

+

!

!

 

||

+

||

||

 

||=

+

x ||= y

See "diff" page...

 

 

 

 

 

 

Other

 

 

 

 

[]

+

[]

[]

 

as

+

as

See "diff" page...

 

,

+

,

,

 

?:

+

(value) ? true : false;

(value) ? true : false;

 

delete

+

delete

delete

 

.

+

.

.

 

in

+

in

in

 

instanceof

+

instanceof

instanceof

See "diff" page...

is

+

is

?


::

+

::

?

 

new

+

new

new

 

{}

+

{}

{}

 

()

+

()

()

 

/

+

/

/

 

:

+

:

See "diff" page...

 

typeof

+

typeof

typeof

See "diff" page...

void

+

void

void

 

 

 

 

 

 

String

 

 

 

 

+

+

+

+

 

+=

+

+=

+=

 

"

+

"

"

 

 

 

 

 

 

XML

 

 

 

 

@

-

@

See "E4X" page...


{}

-

{}

See "E4X" page...

 

[]

-

[]

See "E4X" page...

 

+

-

+

See "E4X" page...


+=

-

+=

See "E4X" page...


delete

-

delete

See "E4X" page...


..

-

..

See "E4X" page...


.

-

.

See "E4X" page...


()

-

()

See "E4X" page...


<>

-

<>

See "E4X" page...

 

 

 

 

 

 

Statements

 

 

 

 

break

+

break

break

 

case

+

case

case

 

continue

+

continue

continue

 

default

+

default

default

 

do...while

+

do...while

do...while

 

else

+

else

else

 

for

+

for

for

 

for...in

+

for...in

for...in

 

for each...in

+

for each...in

See "diff" page...

 

if

+

if

if

 

label

+

label

label

 

return

+

return

return

 

super

+

super([arg1, ..., argN]);
super.method([arg1, ..., argN]);

See "diff" page... 

 

switch

+

switch

switch

 

throw

+

throw

throw

 

try...catch...finally

+

try...catch...finally

try...catch...finally

 

while

+

while

while

 

with

+

with

with

 

 

 

 

 

 

Attribute Keywords

 

 

 

 

dynamic

+

dynamic

See "diff" page...

 

final

+

final

See "diff" page...

 

internal

+

internal

See "diff" page...

 

native

 

[not user accessible]

 

 

override

+

override

See "diff" page...

 

private

+

private

See "diff" page...

 

protected

+

protected

See "diff" page...

 

public

+

public

See "diff" page...


static

+

static

See "diff" page...

 

 

 

 

 

 

Definition keywords

 

 

 

 

... (rest) parameter

+

...rest

See "diff" page...


class

+

class

See "Class Implementations" page...

 

const

+

const

See "diff" page...

 

extends

+

extends

See "diff" page...

 

function

+

function

function

 

get

+

get

See "diff" page...

 

implements

+

implements

See "diff" page...

 

interface

+

interface

See "diff" page...

 

namespace

+

namespace

See "diff" page...

 

package

+

package

See "diff" page...

 

set

+

set

See "diff" page...

 

var

+

var

var

 

 

 

 

 

 

Directives

 

 

 

 

default xml namespace

-

default xml namespace

See "E4X" page...

 

import

+

import

See "diff" page...

 

include

-

include

See "diff" page...

 

use namespace

-

use namespace

See "diff" page...

 

 

 

 

 

 

Namespaces

 

 

 

 

AS3

-

?

?

See "diff" page...

flash_proxy

-

?

?

See "diff" page...

object_proxy

-

?

?

See "diff" page...

 

 

 

 

 

Primary expression keywords

 

 

 

 

false

+

false

false

 

null

+

null

null

 

this

+

this

See "diff" page...

 

true

+

true

true

 

 

 

 

 

 

  • No labels

21 Comments

  1. If you want "" to be converted to '', we are going to have to add escaping logic of some kind.

    I still need to see what the actual implementation of the two types of strings are.

    1. We have a utility method in Jangaroo on github that does such AS3 string character escaping, maybe it helps?

      1. Nice, that is exactly what I was speaking of.

        I still need to figure out how the compiler deals with strings, if there is an special handling in FalconJS that has already been implemented.

  2. I'd like to come back to one of my comments from the corresponding discussion thread:

    What does assigning initial field values in the constructor body have to do with the field being private?

    If anything, it makes a difference whether the right-hand side of the assignment is an immutable value or a mutable one.
    In the first case, you could safely assign the value to the prototype's field and even save some memory at runtime (while losing some CPU cycles for prototype-chain-lookup everytime the instance field is accessed).
    However, if it is a dynamic expression (new Date().getTime()) or evaluated to a mutable object (like e.g. a new Array []), you have to make sure that every object instance receives its own freshly evaluated value, and so the assignment has to be placed in the constructor. And this has to be done for public fields, too!

    For the time being, we should either ignore visibility modifiers completely (at least the compiler has already checked access rights before we even start generating code) or, to avoid name clashes of private members between class inheritance levels at runtime, implement "The Pragmatic Solution" described in my blog post "Simulating ActionScript in JavaScript: Private Members" (please also have a look at the comments).

    One more thing, I'd suggest to talk about "fields", not "variables", to clearly distinguish between local variables (which do not need any special treatment by the compiler) and class member variables (= fields). The suggested solution treats private methods similar, so we might even want to talk about "members".

    1. What makes a field private in the current approach is the JSDoc annotation "@private". The Closure Compiler will treat any field indicated with that annotation as a private, and if any external object tries to access it, it will generate a compiler error.

  3. After having had a closer look at the source code of goog.base(), I must say I am not convinced we should use it.
    The overloading of calling the super constructor or a specific method as well as the dynamic look-up of the super method introduce unnecessary runtime overhead.

    As we know the super class constructor at compile time, calling it can be achieved directly without any utility function. In your example:

    org.apache.flex.Button.call(this);
    

    For implementing method super calls, in the Jangaroo Runtime, I reused the mechanism introduced for private members and "back-up" the super class method to a private method before overriding it. This cannot name-clash, as no private method may have the same name as another non-static method of the same class.
    Then, the call

      override protected function foo(x:String):int {
        return super.foo(x + "-sub");
      }
    

    is simply converted to something like

      MyClass.prototype.foo$3 = MyClass.prototype.foo; // override => back-up super class method
      MyClass.prototype.foo = function(x) {
        return this.foo$3(x + "-sub"); // invoke method foo of super class
      }
    

    where "$3" corresponds to the inheritance level (again see "Simulating ActionScript in JavaScript: Private Members").
    This version is straight-forward and does not introduce any runtime-overhead at all, not even an apply().

    1. I'm getting mixed signals. In my original code I had the solution you suggest (with the "call" in the constructor), but was advised to take the goog.base() route. Now we want to go back? That's fine with me, but at some point we need to agree on one approach or another.

      Also, the Closure Tools work together to get the best out of JavaScript. The Closure Compiler is an actual compiler that parses and re-writes the code. This means that any overhead by using a method to call the super() is probably negligible and if weighed against maintainability and ease of use/readability, I think calling goog.base() wins hands down. 

      1. > at some point we need to agree on one approach or another.

        I think this is what we are trying to do Erik. (smile)

        I wish I could contribute in the discussion but I have no experience with js, and reading a bunch of blogs with opinions isn't going to help either.

        I'll focus on what I know.

      2. I was certainly not the one who advised to take the goog.base() route. Anyone remembers who said that? Maybe Gordon?
        But you are right, I read over this part of goog.base()'s documentation:

        At compile-time, the compiler will do macro expansion to remove a lot of the extra overhead that this function introduces. The compiler will also enforce a lot of the assumptions that this function makes, and treat it as a compiler error if you break them.

        Still I don't see why the "uncompiled" implementation has to be so wasteful.

        ...if weighed against maintainability and ease of use/readability, I think calling goog.base() wins hands down.

        I don't agree here. In my opinion, making goog.base() an overloaded function for both super constructor and super method calls with different parameter semantics does not really make it easy to understand. And I find the generated code proposed above quite easy to read. Especially this.foo$3(x + "-sub") resembles the original super.foo(x + "-sub") better than goog.base(this, "foo", x + "-sub"), where the method name becomes a string parameter.

        GCC has to reverse-engineer class-based-OO constructs from JavaScript code. As we start from ActionScript code that already has a class-based structure, why not generate the target format directly? At least in cases where it simplifies things, we should do so. For require(), provide() and maybe inherit(), I think the goog library provides real value.
        What I can't say is, will GCC work / optimize correctly if we cherry-pick goog library usages? I guess we will have to experiment. That's what I proposed on the mailing list: I'll build a prototype with Jangaroo using (parts of) the goog library, so we bring the two solutions we have right now closer to each other.
        Anyway, Mike promised that his solution will be very modular, so it should be easy (or at least possible) to change generated code patterns later on.

  4. Solutions from Jangaroo Runtime:

     

    ActionScript

    JavaScript

    int()

    int(x)

    (x >> 0)

    uint()

    uint(x)

    (x >>> 0)

    I think it is not necessary to have all operators and statements in the table, since most of them have exactly the same semantics in ActionScript and JavaScript. To have a more concise table, we should only list those language constructs that actually need rewriting. For example "for each" and all XML-related operators are not present in most JavaScript engines (only Firefox implemented E4X and Mozilla announced that they will probably discontinue supporting it).
    Keywords like "protected" need a context so that you can decide how to rewrite them, like you did with the "Fields" group.

    1. Agreed.

      But in order to create a table of stuff that needs changing (or annotation), I thought it better to start with a complete list and remove agreed upon items than creating one of only stuff that needs 'changing' and risk missing some.

  5. So far I have 60 unit tests on my new framework testing binary, unary, member access etc.

    I think we should have this table left as is and also make SURE that all ActionScript elements that are possible with the Falcon compiler are listed.

    When this project takes off, which it most certainly will, we need all developers that are just writing applications that are being cross compiled to know the conversion or lack there of with every ActionScript element.

    What should happen is when we agree on the translated JavaScript it will move to another page table on the wiki of "agreed upon" semantics. This way we have a finite grasp of what we are doing.

  6. As far as I know, the global JavaScript functions Array, Boolean, String, Number, Object work like their ActionScript counterparts: when invoked without new, they still create a new object, effectively converting their argument into the target type. So I think we can just reuse the JavaScript functions.
    The only situation where we have to take care is not to generate code for all other type casts, which unfortunately look exactly like a function call. But I hope Falcon can tell the difference.

    Concerning a utility function for trace, this Jangaroo runtime part might help; it solves some cross-browser concerns and additionally allows to access different console methods if available without changing the method signature of trace. joo.getQualifiedObject is only used to access global properties in any environment and can simply be replaced by using this in the global scope.

    1. Well, I'm quite deep in Falcon stuff and I am sure it won't be a problem figuring out casts from function calls. We have resolveCalledExpression() on the IFunctionCallNode so this means if it returns an IClassDefinition without the IKewordNode child, its a cast. If it returns an IFunctionNode its a constructor call.

    2. Playing around with our trace implementation, I just found a bug and fixed it. Will be in the git repo any minute.

  7. Concerning &&= and ||= (where you put in the question marks): they do not exist in JavaScript and have to be emulated.
    The straight-forward emulation for x &&= y is of course x = x && y.
    Note however that this way, x is evaluated twice, which might make a difference. E.g. take foo()[bar()] &&= baz(): ActionScript evaluates functions foo and bar once, but the emulation foo()[bar()] = foo()[bar()] && baz() would evaluate them twice. In similar situations, I introduced a helper method, but this is not so easy here, as x appears on the left-hand side of an assignment as well as on the right-hand side. You would have to do trickier things like

    var $aux_1, $aux_2;
    $aux_1[$aux_2] = ($aux_1 = foo())[$aux_2 = bar()]) && baz();
    

    But it is probably not worth the effort. I think for such rarely used operators, the "almost the same" straight-forward emulation might suffice.

    Edit: Thanks Olaf Kummer for the correction that baz was evaluated non-short-circuit in my first version using a helper method! To keep the helper method approach, we would have needed to pass a function to the helper method, making that approach too expensive.

    1. So, the final consensus was to use x = x && y?

      1. Frank says: "a 'cleaner' solution is probably not worth the effort", and I tend to agree with "less effort" solutions. So, unless this solution presents some problem on your end, I think there is "consensus" ;-)

        1. If that means consensus for the first iteration, I'd agree.
          As soon as we have everything working in principle, we should have another turn on the dirty details like this one.
          We need to introduce auxiliary variables for other constructs like for each anyway. As soon as the code generator can generate aux vars (find out which var name is "free"), the cleaner solution mentioned above should be no problem and then we should implement it.

          Talking about for each, we simulate it like so:

            for each (var slot in o)
              result.push(slot);
          

          becomes

            for (var $1 in o){
              var slot = o[$1];
              result.push(slot);
            }
          

          Mind the curly braces that may need to be added when a single statement is complemented.
          Of course, as an optimization, we could inline variable slot, but we can also leave that to the JavaScript optimizer. The important part is that when slot occurred multiple times, o[$1] must only be evaluated once.
          Also note that the loop variable may be declared elsewhere, like so:

            var slot;
            for each (slot in o)
              result.push(slot);
          

          becomes

            var slot;
            for (var $1 in o){
              slot= o[$1];
              result.push(slot);
            }
          

          In the most complex case, o is a complex expression that also must only be evaluated once (for efficiency as well as to keep semantics if it has side effects), so we need two aux vars:

            for each (var slot in compute())
              result.push(slot);
          

          becomes

            var $2;
            for (var $1 in $2 = compute()) {
              var slot = $2[$1];
              result.push(slot);
            }
          
          1. I don't want to under think this but what you have added for the for each doesn't seem that hard as it stands.

            When visiting a for each statement we already have scope, inner and outer. These means we can already figure out what vars are "in" scope so we don't trash them, as you put it auxiliary vars.

            So to answer, we already have means to do some hard core semantic and scope checking. The Falcon compiler framework is like fine dining already. (smile)

            1. Good to hear, Mike! Well then the correct solution for &&= and ||= shouldn't be that hard, either. The only thing that comes on top of creating aux vars is to split the LHS expression into x[y], if applicable (which of course includes x.foo, being the same as x['foo']). If splitting the expression like that is not possible, it must be a simple expression like a variable that cannot have side-effects, and thus falling back to the straight-forward version results in the correct semantics.