Download

Go here to download ready-to-run and source distributions of Jangaroo. more...

Tuesday, December 6, 2011

Simulating ActionScript in JavaScript: Private Members Cont'd

In the first blog post about simulating private members in ActionScript, I compared different solutions to represent private members in JavaScript. The solution implemented by Jangaroo is to rename private members, so that they do not name-clash with private members of the same class, defined on a different inheritance level.
Bernd Paradies asked how Jangaroo solves untyped access to private members. Since the answer is rather extensive, I'll dedicate this follow-up post to the topic.

The short answer is: it's the nature of the beast. Untyped access to private members cannot be detected at compile time. However, potential access to private members can be detected at compile time and generate code that repeats the check at runtime, although Jangaroo does not implement this at the moment.

Typed Private Member Access
Let's recall the typical way to access private members, which can easily be detected by the Jangaroo compiler:

 1 public class Foo {
 2   private var foo:String = "bar";
 3   public function getFoo():String {
 4     return foo;
 5   }
 6 }

As foo in line 4 is resolved to a field of the class, and there is no implicit this access in JavaScript, the Jangaroo compiler adds this. before foo. Also, as field foo is declared private, the compiler renames it to foo$1 (see previous blog post) and thus generates the following JavaScript code for the body of method getFoo():

 4     return this.foo$1;

Where is My Type?
Now consider the following code (which does not really make sense, but illustrates the point):

 1 public class Foo {
 2   private var foo:String = "bar";
 3   public function getFoo():String {
 4     var untypedThis:Object = this;
 5     return untypedThis.foo;
 6   }
 7 }

Of course, in this simple example it would still be possible to determine the runtime type of untypedThis statically, but it is easy to imagine a situation where this is not possible. In the current Jangaroo implementation, the following JavaScript code would be generated for the body of method getFoo():

 4     var untypedThis = this;
 5     return untypedThis.foo;

As you can see, the compiler fails to detect that foo actually refers to the private member, does not rename the access to foo$1, and thus the undefined value of untypedThis.foo would be returned.

A (partial) solution is to let the compiler detect any expression of the form untyped.private-member and generate code that takes into account the runtime type of untyped, like so:

 5     return untyped[untyped instanceof Foo ? 'foo$1' : 'foo'];

Trusting today's JavaScript JIT compilers' optimizations, this code should be moved to a utility function to avoid double evaluation of the untyped expression (which could have side effects):

 5     return Foo.$class.get(untyped, 'foo');
 
where Foo.$class provides access to Jangaroo's "meta class" of Foo, and get() would be implemented there like this:

  public function get(object:Object, property:String):* {
    return object[object instanceof this.publicConstructor ?
      property + this.inheritanceLevel : property];
  }

The Weak and the Wicked
Looking at ActionScript carefully, you'll notice that the identifier or expression left of the dot does not even have to be untyped, but may just be typed with a more general type, and the same scenario may apply. For example, assume our class Foo is a subclass of dynamic class Bar, we could replace Object by Bar, and the example would still work! Even if Bar is not dynamic, we could access the private member foo through a local variable of type Bar using square brackets, like so:

public class Foo extends Bar {
  private var foo:String = "bar";
  public function getFoo():String {
    var weaklyTypedThis:Bar = this;
    return weaklyTypedThis['foo'];
  }
}

The Flex compiler does not complain, and at runtime, in Flash, indeed the private member is accessed. Compare this to the following example, where we try to access the private member of a superclass:

public class Baz extends Foo {
  public function Baz() {
    super();
    var superThis:Foo = this;
    trace(superThis['foo']); // is undefined, not "bar"!
  }
}

Maybe you'll be surprised that the result is not any kind of access violation, but simply undefined—the private member of the superclass is simply not visible for the subclass! This is well emulated by Jangaroo through renaming private members. The Jangaroo compiler just has to take care to detect every correct access of a private member and rename the property upon access.

Turing Strikes Again
Things get even nastier when the property is a dynamic expression. Consider

  return this[veryComplicatedComputation()];

Here, too, we'd have to generate

  return Foo.$class.get(this, veryComplicatedComputation());

and extend the utility function by a runtime check whether the property is actually a private member name:

  public function get(object:Object, property:String):* {
    return object[object instanceof this.publicConstructor &&
      this.getMemberDeclaration("private", property) ?
        property + this.inheritanceLevel : property];
  }

Summary
Taken together, the important criterion when to generate code for possible private member access is not whether the left-hand expression of the dot is untyped, but the compiler has to check whether the complete property access is not typed. This is the case exactly when the property is not a compile-time constant or when the property cannot be resolved statically within the type of the expression, and in any case, the type of the expression must be a supertype of the current class or the current class itself.

To wrap up, the compiler would have to generate a call to the dynamic access function if and only if
  1. the left-hand expression could have a runtime type compatible to the current class and 
  2. the property expression could be one of the current class's private members.
Here, could means that it may be, but is not certain at compile time. If both conditions are certain at compile time, the compiler knows for sure that the code represents access to a private member, and can simply use the renamed property.

One more thing: I have been talking about read access of private members. The same strategy would have to be applied when writing members, resulting in another utility function set(object, property, value).

Optimize?
Of course, we could optimize runtime performance by using specific utility functions for the following three different cases:
  1. The property is definitely a private member, but the left-hand expression may or may not be of the current class: check instanceof only.
  2. The left-hand expression is definitely of the current class, but the property may or may not be a private member: check the property only.
  3. Both the left-hand expression may or may not be of the current class, and the property may or may not be a private member: check instanceof and the property.
However, my guess is that this optimization is not necessary. Firstly, checking instanceof and whether some string is contained in a fixed and not very large set are cheap operations. Secondly, when using ActionScript (and not untyped JavaScript), you should avoid untyped access of properties whenever possible. Thus, the situation is a rare case, and would only be implemented for full ActionScript semantic compatibility. If the developer wants better performance, she can either insert a type cast to convert the property access to a typed one, or move dynamic properties to a dedicated object whose type is statically incompatible with the current class (e.g. use Dictionary), and thus no runtime check would be generated.
If this solution proves to inflict significant runtime overhead, the compiler should issue a warning when it has to generate runtime checks.

4 comments:

Philippe said...

Interesting approach.

As a side note, did you check Jangaroo generated JS against Google Closure? It is a pretty aggressive JS optimizer, especially the advanced mode which is very hard to support.

Frank said...

Philippe,
almost forgot to answer here: yes, we tried Google Closure, and made some modifications to the Jangaroo compiler so Closure can be used without any additional command line parameters. So far, we only got the "simple" mode to produce working code, not the "advanced" mode. In case of Jangaroo-generated code without comments or white-space, even "advanced" mode only reduces JS code size by 20-30%, while gzip compression results in about 85% reduction. Also, the difference between gzipping directly and gzipping after optimizing via Closure is negligible, so doing both is not really worth the effort.
Thus, I do not really see value in using Closure. Maybe we could use it to spare the Jangaroo compiler taking care of removing comments and white-space and get a bit extra code size reduction "for free".

StarbuckZero said...

Does Jangaroo support Flash sockets or websockets in some form?

Frank said...

Hi StarbuckZero,
good question, but since it is rather unrelated to the blog post, I re-posted and answered it on the jangaroo-users group:
http://groups.google.com/group/jangaroo-users/browse_thread/thread/5f90ef2606dda2c4

Greetings,
-Frank-