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

Compare with Current View Page History

Version 1 Current »

Even ActionScript doesn't allow true circular dependencies.  In other words you can't do this:

public class A extends B

public class B extends A

 

But the Google Closure Compiler (GCC) is even more strict.  It will not allow:

public class Child {
public var parent:Parent;
}

public class Parent {
public var child:Child;
}

This is allowed in Flash/AIR because both classes can be constructed by the runtime. The only type-checks for parent and child properties happens after initialization when code actually starts to run. In GCC, because Parent and Child are in different .js files, and it can't tell which one to load first, and there is no way to tell GCC that a dependency isn't needed at initialization time, GCC outputs an error.  That's because Child.js starts with:

goog.provide('Child');
goog.require('Parent');

and Parent.js starts with:

goog.provide('Parent');
goog.require('Child'); 

The output has to look like this because you don't know whether Child.js will be used first and need to bring in Parent or the other way around.

In the FlexJS/FalconJX compiler, we have provided a set of compiler options and directives to help manage this problem, but the theoretical solution is to refactor the code to eliminate circularities.  However, this isn't always possible with legacy code without creating backward compatibility issues.

-remove-circulars option

This brute-force compiler option tells the compiler to look at the order that these classes will be used and delete one of the goog.requires from one of those files.  So if your code first references the Child class, the compiler will remove the goog.requilre('Child') from the Parent.js since it knows that some other code also had to have a goog.requires('Child') in it so Child will get loaded.

@flexjsignoreimport <fully-qualified classname>

This is a directive you put in an ASDoc block for your class.  This directs the compiler to not generate a goog.require for that class.

-js-output-optimization=<option>

Often, goog.requires are generated by code that is coercing or casting a plain object to some type.  Since JavaScript doesn't really know about types, you can often skip these coercions and eliminate some of the goog.requires that cause GCC to output an error.  The options are:

skipAsCoercions - This means that use of (instance as SomeClass) is ignored and not output by the compiler.  Be sure that you are not relying on "as" to return null if the type does not match.

skipFunctionCoercions - This means that use of SomeClass(instance) is ignored and not output by the compiler.  Again, be sure that you are not relying on the function to throw an error if the type does not match.

Refactoring Code

The true answer is to refactor code.  Interfaces should be used instead of direct dependencies on classes.  So the original example would look more like:

public class Child implements IChild{
public var parent:IParent;
}

public class Parent implements IParent {
public var child:IChild;
}

public interface IChild {}

public interface IParent {} 

This seems like too much work for rapid prototyping, which is why we provide the other options above.

 

  • No labels