You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

The FalconJx program is a prototype of a javascript cross compiler.

Q: Is FalconJx the same as FalconJS?

A: No, FalconJx has nothing to do with FalconJS other than it uses the same compile pattern, program startup and backend configuration type setup.

From the standpoint of a replacement, that is up for debate. From the standpoint of development, the project was started out of a necessity to deal in reality on a programming capability level.

The project doesn't even have a name, the FalconJx is to denote the implementation difference. Also note, this project may never be accepted as a solution for cross compiling ActionScript to JavaScript but, it's what I will be working on.

Q: How does it work?

A: Basically we are utilizing the fantastic AST IASNode structure, traversing the nodes and visiting them individually with an emitter. The difference here is the AST walker is a hand written implementation that can easily be maintained and optimized per individual specs.

Compilation Steps

  • Main method is called with compile arguments like MXMLC
  • A backend instance is created that acts as a factory to create the source file handler, configurator, javascript target, AST walker, JavaScript emitter and node switch strategy and the JavaScript writer.
  • The configuration instance is created and the project is configured with the run-time arguments and defaults.
  • The main compile() method is called.
  • The file handler is setup for .as and .mxml files.
  • The target file is setup. The target file is the main application file used just like MXMLC expects. The target file is the entry point to the application.
  • Once everything is setup and there are no configuration and target errors present, the JavaScript target is built.
  • A JSTarget instance is created and the build() method is called.
  • Once the build() method is called, the compiler runs in multi-threaded mode finding all reachable compilation units and proceeds to call the ASParser on them created their AST trees.
  • If the JSApplication artifact is created successfully a JSWriter is created for each compilation unit and an AST walker is created to traverse each compilation unit's AST tree to emit JavaScript source code dependent on the visitor/emitter implementation.
  • After each compilation unit is traversed, the output bytes of the emitter are then saved to it's corresponding .js file in the source output directory.
  • If all is successful, the compiler will exit with a 0 exit code meaning success and all ActionScript files were cross compiled to JavaScript.

The above is a very minimized outline of the program flow within the compiler. I would say about 2/3 closely resembles the original FalconJS compiler with setup and compilation unit management. The difference as stated above is how the JavaScript is generated.

Unit Tests

Unit tests are an essential part of developing any software and when dealing with a cross compiler it becomes essential to test every facet of production.

From what I looked at it would be very hard to unit test the original FalconJS code. It's written with a Bottom Up Rewritter (JBurg) and is very hard for intermediate to even advanced programmers to understand.

Within 2 days I was able to generate over 80 unit tests for the prototype cross compiler. Most expressions and statements are unit tested already. (binary, unary, literals, statements such as if, while, switch, etc.).

For the first unit tests implemented the pattern follows close to what we are testing in MXML.

  • Create a string class with the expression or statement injected within.
  • Load up the configuration and flex-config.
  • Pass the arguments to the compiler.
  • Save the string class to a temporary file.
  • Load the temp file into the Falcon parser.
  • Parse the class for the root IFileNode.
  • Use a method called from the test method to get the specific node such as a + b, you will get an IBinaryOperatorNode.
  • Call the corresponding visitor method to write out the JavaScript string to the output buffer.
  • Test the buffer against the expected output.
@Test
public void testVisitBinaryOperatorNode_Plus()
{
    IBinaryOperatorNode node = getBinaryNode("a + b");
    visitor.visitBinaryOperator(node);
    assertOut("a + b");
}

The goal of this design is granularity. With the ability to test each expression, statement and block individually, virtually all spaghetti code messes are resolved.

Note: Most of these base test output JavaScript that is virtually identical to ActionScript. This means ActionScript could as easily be output from the emitter since it proves that the walker is semantically traversing the ActionScript AST correctly. Using this same framework, different language output could be implemented, would you want to do that? Only time will tell.

Composition

The main goal of this prototype is for composition of emitter strategies. There are specific levels of node handlers that have been implemented.

  1. Compilation unit level walker that traverses the whole Class/Interface AST tree. This API is represent with the IASNodeStrategy interface with one method handle(IASNode node).
  2. A before and after strategy that will be called before a node is handled and after a node is handled. This allows for correct block indenting, semi colon handling and pre and post symbol handling.
  3. An emitter API that has specific write methods that can be overridden.
  • No labels