Hi Andrew,

Thanks for looking into this.

One of the things to keep in mind about the compiler that isn't immediately 
obvious is that the JS compiler can be thought of almost as a subclass of the 
SWF compiler.  It isn't truly that way in the code, but the way it is now, even 
if you don't want SWF output, we actually run the most of the SWF compiler 
tasks on the source code before generating the JS because the transpiler's AST 
walk (that uses the Emitters to generate the JS) does not have the smarts for 
semantic analysis and similar things like binding and watcher analysis.

The clue to that is which Eclipse projects or jars the various source files 
belong to.  We will try not to introduce any files from the compiler-jx 
project/jar into the compiler and compiler-common project/jars.  Doing so could 
create build order/dependency issues.  And thus, we don't want to access the 
Emitters from the WatcherAnalyzer, nor have the WatcherAnalyzer know that it is 
talking to a RoyaleJSProject instead of a RoyaleProject (the latter which is 
designed for SWF output).

And, even if you did find a way to access the Emitter, I think the same Watcher 
database is used for SWF output as well, so you don't always want to decorate 
the name of private variables in the data structure.

After a quick think, I would suggest doing a bit more digging.  I noticed that 
in the snippet you posted, a getter function appeared to be correctly 
generated.  Is that part of the Watcher data or the Binding data?  In the 
ActionScript code (DataBindingBase and subclasses) the Binding data is supposed 
to discriminate between "simple" property chains that it can create more 
efficient Bindings for vs GenericBinding that can handle more complex cases.  
The idea is that in some cases, we can't just access the value by an 
instance.propName lookup and a getterFunction is generated instead.   This is 
absolutely required in SWF code since external classes like the Binding classes 
can't access private members of the class it is watching.  It has always felt 
like a "cheat" to me that Binding pokes holes in the private access protection 
for Bindings, but that's pretty much the only way we can do it in SWF code 
(which also sort of means you lose the advantage of binding to private 
variables in the first place).  In fact, I think one of the optimizations for 
Royale binding will be that bindings will be faster/smaller on public variables 
than private variables.

So, having said all that, one possible solution is to have the ActionScript 
code use the getter function instead of the propertyName for private variables. 
 I thought that the transpiler did not always output a getter function if 
public property chains could access the value.  The AS code could then know to 
use a function instead of the property name.  If some future version of JS 
actually does provide private access protection someday, we'll be glad we did 
this.  Right now, the Royale JS binding code has been relying on direct access 
to private variables, which is also a "cheat", IMO.

Another possible approach is to change what is stored in the Watcher database.  
Add new fields to store the fact that the property is private.  Then in 
MXMLRoyaleEmitter, the watcher output code should have access to an emitter, 
and there is no need to access compiler-jx classes from the WatcherAnalyzer.  
This approach (just adding more info in the shared Analyzer code instead of 
generating code) is probably more platform/output-independent.

Or maybe you'll need both.

Hope that made sense.  Good luck,
-Alex


On 12/6/18, 6:30 AM, "Frost, Andrew" <andrew.fr...@harman.com> wrote:

    Hi all
    
    I found recently that binding had stopped working when we were using 
private variables, for example:
      <test:TestComponent id="testing" width="100" height="20"  
boundValue="{_model.value}">
    where we declare:
    private var _model : Model = Model.getModel();
    
    However it works with protected/public variables..
    
    The reason is clear when you look at the generated _bindings array, which 
contains:
      function() { return this.test_MainView__model.value; },
    and then
      "_model",
      "valueChange",
    
    So basically the watcher list is using "_model" whereas the property is 
actually generated as "test_MainView__model".
    
    Looking at the compiler code, there's a function on the emitter which does 
this translation:
          public String formatPrivateName(String className, String name) {
                return className.replace(".", "_") + "_" + name;
          }
    
    However I can't work out how to get the watcher analyser class to access 
the emitter.. I think this is where the logic should go, because within 
"WatcherAnalyzer. analyzeIdentifierNode" we're getting the name as a string and 
creating the watcher object based on this string. So I want to do:
                name = def.getBaseName();
                if (def.isPrivate() && project.getAllowPrivateNameConflicts())
                {
                      name = 
getEmitter().formatPrivateName(def.getParent().getQualifiedName(), name);
                }
    
    My issue is that there's no access that I can see to the Emitter.
    
    So I thought I'd ask:
    
      1.  does anyone know how I could actually get a valid reference to the 
emitter from this point in the code? i.e. just through the JSRoyaleProject 
object from what I can see..
      2.  or is there a better place where this should be? e.g. within 
getBaseName itself, or where the base name is set up on the definition..?
      3.  or could we move the "formatPrivateName" method to somewhere more 
accessible?
      4.  or can I just reproduce the "formatPrivateName" functionality here, 
and add comments so that future maintainers know to update two locations in 
case this changes?
    
    The last option is simplest but very hacky.. but would appreciate thoughts 
on the best approach.
    
    
    thanks
    
       Andrew
    
    
    
    

Reply via email to