Authors

Tuesday, December 6, 2011

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):

Of course, in this simple example it would still be possible to determine the runtime type of untypedThisstatically, 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:

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:

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:

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

the left-hand expression could have a runtime type compatible to the current class and

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 functionset(object, property, value).

Optimize?
Of course, we could optimize runtime performance by using specific utility functions for the following three different cases:

The property is definitely a private member, but the left-hand expression may or may not be of the current class: check instanceof only.

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.

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 instanceofand 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.