|
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) |
|
isFinite() |
+ |
isFinite(value) |
isFinite(value) |
|
isNaN() |
+ |
isNaN(value) |
isNaN(value) |
|
isXMLName() |
+ |
isXMLName(value) |
|
|
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) |
|
uint() |
+ |
uint(value) |
uint(value) |
|
unescape() |
+ |
unescape(value) |
unescape(value) |
|
Vector() |
+ |
result = Vector.<type>(valueArray); |
|
|
XML() |
+ |
XML(value) |
|
|
XMLList() |
+ |
XMLList(value) |
|
|
|
|
|
|
|
Classes |
|
|
|
|
ArgumentError |
+ |
ArgumentError |
ArgumentError |
|
arguments |
+ |
arguments |
arguments |
|
Array |
+ |
Array |
Array |
|
Boolean |
+ |
Boolean |
Boolean |
|
Class |
+ |
- |
- |
|
Date |
+ |
Date |
Date |
|
DefinitionError |
+ |
DefinitionError |
DefinitionError |
|
Error |
+ |
Error |
Error |
|
EvalError |
+ |
EvalError |
EvalError |
|
Function |
+ |
Function |
Function |
|
int |
+ |
int |
int |
|
JSON |
+ |
JSON |
JSON |
|
Math |
+ |
Math |
Math |
|
Namespace |
+ |
Namespace |
Namespace |
|
Number |
+ |
Number |
Number |
|
Object |
+ |
Object |
Object |
|
QName |
+ |
QName |
|
|
RangeError |
+ |
RangeError |
RangeError |
|
ReferenceError |
+ |
ReferenceError |
ReferenceError |
|
RegExp |
+ |
RegExp |
RegExp |
|
SecurityError |
+ |
SecurityError |
SecurityError |
|
String |
+ |
String |
String |
|
SyntaxError |
+ |
SyntaxError |
SyntaxError |
|
TypeError |
+ |
TypeError |
TypeError |
|
uint |
+ |
uint |
uint |
|
URIError |
+ |
URIError |
URIError |
|
Vector |
+ |
Vector |
|
|
VerifyError |
+ |
VerifyError |
VerifyError |
|
XML |
+ |
XML |
|
|
XMLList |
+ |
XMLList |
|
|
|
|
|
|
|
Arithmetic |
|
|
|
|
+ |
+ |
+ |
+ |
|
-- |
+ |
-- |
-- |
|
/ |
+ |
/ |
/ |
|
++ |
+ |
++ |
++ |
|
% |
+ |
% |
% |
|
* |
+ |
* |
* |
|
- |
+ |
- |
- |
|
|
|
|
|
|
Arithmetic compound assignment |
|
|
|
|
+= |
+ |
+= |
+= |
|
/= |
+ |
/= |
/= |
|
%= |
+ |
%= |
%= |
|
*= |
+ |
*= |
*= |
|
-= |
+ |
-= |
-= |
|
|
|
|
|
|
Assignment |
|
|
|
|
= |
+ |
= |
= |
|
|
|
|
|
|
Bitwise |
|
|
|
|
& |
+ |
& |
& |
|
<< |
+ |
<< |
<< |
|
~ |
+ |
~ |
~ |
|
| |
+ |
| |
| |
|
>> |
+ |
>> |
>> |
|
>>> |
+ |
>>> |
>>> |
|
^ |
+ |
^ |
^ |
|
|
|
|
|
|
Bitwise compound assignment |
|
|
|
|
&= |
+ |
&= |
&= |
|
<<= |
+ |
<<= |
<<= |
|
|= |
+ |
|= |
|= |
|
>>= |
+ |
>>= |
>>= |
|
>>>= |
+ |
>>>= |
>>>= |
|
^= |
+ |
^= |
^= |
|
|
|
|
|
|
Comment |
|
|
|
|
/**/ |
+ |
/**/ |
/**/ |
|
// |
+ |
// |
// |
|
|
|
|
|
|
Comparison |
|
|
|
|
== |
+ |
== |
== |
|
> |
+ |
> |
> |
|
>= |
+ |
>= |
>= |
|
!= |
+ |
!= |
!= |
|
< |
+ |
< |
< |
|
<= |
+ |
<= |
<= |
|
=== |
+ |
=== |
=== |
|
!== |
+ |
!== |
!== |
|
|
|
|
|
|
Logical |
|
|
|
|
&& |
+ |
&& |
&& |
|
&&= |
+ |
x &&= y |
|
|
! |
+ |
! |
! |
|
|| |
+ |
|| |
|| |
|
||= |
+ |
x ||= y |
|
|
|
|
|
|
|
Other |
|
|
|
|
[] |
+ |
[] |
[] |
|
as |
+ |
as |
|
|
, |
+ |
, |
, |
|
?: |
+ |
(value) ? true : false; |
(value) ? true : false; |
|
delete |
+ |
delete |
delete |
|
. |
+ |
. |
. |
|
in |
+ |
in |
in |
|
instanceof |
+ |
instanceof |
instanceof |
|
is |
+ |
is |
? |
|
:: |
+ |
:: |
? |
|
new |
+ |
new |
new |
|
{} |
+ |
{} |
{} |
|
() |
+ |
() |
() |
|
/ |
+ |
/ |
/ |
|
: |
+ |
: |
|
|
typeof |
+ |
typeof |
typeof |
|
void |
+ |
void |
void |
|
|
|
|
|
|
String |
|
|
|
|
+ |
+ |
+ |
+ |
|
+= |
+ |
+= |
+= |
|
" |
+ |
" |
" |
|
|
|
|
|
|
XML |
|
|
|
|
@ |
- |
@ |
|
|
{} |
- |
{} |
|
|
[] |
- |
[] |
|
|
+ |
- |
+ |
|
|
+= |
- |
+= |
|
|
delete |
- |
delete |
|
|
.. |
- |
.. |
|
|
. |
- |
. |
|
|
() |
- |
() |
|
|
<> |
- |
<> |
|
|
|
|
|
|
|
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 |
|
|
if |
+ |
if |
if |
|
label |
+ |
label |
label |
|
return |
+ |
return |
return |
|
super |
+ |
super([arg1, ..., argN]); |
|
|
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 |
|
|
final |
+ |
final |
|
|
internal |
+ |
internal |
|
|
native |
|
[not user accessible] |
|
|
override |
+ |
override |
|
|
private |
+ |
private |
|
|
protected |
+ |
protected |
|
|
public |
+ |
public |
|
|
static |
+ |
static |
|
|
|
|
|
|
|
Definition keywords |
|
|
|
|
... (rest) parameter |
+ |
...rest |
|
|
class |
+ |
class |
|
|
const |
+ |
const |
|
|
extends |
+ |
extends |
|
|
function |
+ |
function |
function |
|
get |
+ |
get |
|
|
implements |
+ |
implements |
|
|
interface |
+ |
interface |
|
|
namespace |
+ |
namespace |
|
|
package |
+ |
package |
|
|
set |
+ |
set |
|
|
var |
+ |
var |
var |
|
|
|
|
|
|
Directives |
|
|
|
|
default xml namespace |
- |
default xml namespace |
|
|
import |
+ |
import |
|
|
include |
- |
include |
|
|
use namespace |
- |
use namespace |
|
|
|
|
|
|
|
Namespaces |
|
|
|
|
AS3 |
- |
? |
? |
|
flash_proxy |
- |
? |
? |
|
object_proxy |
- |
? |
? |
|
|
|
|
|
|
Primary expression keywords |
|
|
|
|
false |
+ |
false |
false |
|
null |
+ |
null |
null |
|
this |
+ |
this |
|
|
true |
+ |
true |
true |
|
|
|
|
|
|
21 Comments
Michael Schmalle
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.
Frank Wienberg
We have a utility method in Jangaroo on github that does such AS3 string character escaping, maybe it helps?
Michael Schmalle
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.
Frank Wienberg
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".
Erik de Bruin
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.
Frank Wienberg
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:
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
is simply converted to something like
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()
.Erik de Bruin
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.
Michael Schmalle
> at some point we need to agree on one approach or another.
I think this is what we are trying to do Erik.![(smile)](/confluence/s/34gb19/8804/10mvnxf/_/images/icons/emoticons/smile.svg)
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.
Frank Wienberg
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:
Still I don't see why the "uncompiled" implementation has to be so wasteful.
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 originalsuper.foo(x + "-sub")
better thangoog.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.
Frank Wienberg
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.
Erik de Bruin
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.
Michael Schmalle
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.
Frank Wienberg
As far as I know, the global JavaScript functions
Array
,Boolean
,String
,Number
,Object
work like their ActionScript counterparts: when invoked withoutnew
, 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 oftrace
.joo.getQualifiedObject
is only used to access global properties in any environment and can simply be replaced by usingthis
in the global scope.Michael Schmalle
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.
Frank Wienberg
Playing around with our
trace
implementation, I just found a bug and fixed it. Will be in the git repo any minute.Frank Wienberg
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 coursex = x && y
.Note however that this way,
x
is evaluated twice, which might make a difference. E.g. takefoo()[bar()] &&= baz()
: ActionScript evaluates functionsfoo
andbar
once, but the emulationfoo()[bar()] = foo()[bar()] && baz()
would evaluate them twice. In similar situations, I introduced a helper method, but this is not so easy here, asx
appears on the left-hand side of an assignment as well as on the right-hand side. You would have to do trickier things likeBut 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.Michael Schmalle
So, the final consensus was to use
x = x && y
?Erik de Bruin
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" ;-)
Frank Wienberg
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:becomes
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:
becomes
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:becomes
Michael Schmalle
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)](/confluence/s/34gb19/8804/10mvnxf/_/images/icons/emoticons/smile.svg)
Frank Wienberg
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 intox[y]
, if applicable (which of course includesx.foo
, being the same asx['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.