You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@royale.apache.org by "Frost, Andrew" <An...@Harman.com> on 2018/12/06 14:29:58 UTC

Binding to private variables

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




Re: Binding to private variables

Posted by Alex Harui <ah...@adobe.com.INVALID>.
Hi Andrew,

Good point about users adding their own ValueChangeEvent listeners.  If we can detect a private variable is bindable and not rename it, that sounds like the solution that will work in the most places.

My 2 cents,
-Alex

On 12/10/18, 3:13 AM, "Frost, Andrew" <An...@Harman.com> wrote:

    Hi all
    
    Thanks for the inputs.. just getting round to going through this.
    
    So I think we have two different approaches:
    
    1) Ensure we correctly generate the property name to be used in the Watcher object.
    This would be a fix in the emitter code as per Greg's suggestions, so that the Watcher would be correctly checking for a valueChange event coming from the correct property.
    Greg you mentioned about the ValueChangeEvent being dispatched with the undecorated name of the property, but that's still fine as we're not listening out for that at this particular point: instead we're listening to changes that are being made on the object that's assigned to this property. However, I'm not sure what would happen if that property starts off as undefined/null and is then assigned at a later point, as in that case the watcher should be adding a listener to the parent object to spot when the property is set.. ah, having just checked it, yes you're right, this higher-level assignment is missed unless the property setter's event is also updated to the decorated property name...
    So the changes required are:
    a) in our list of bindings, we need to use the longer name
    b) in our dispatching of ValueChangeEvents, we need to use the longer name
    Although this (b) then also means we can't manually write code to listen to the changes, it only works within the binding framework..
    
    2) For bindable variables, don't use the decorated name for private variables.
    This is roughly what Alex suggests due to the longer strings that would then be generated throughout the binding code..
    My slight amendment to this is that we can do it automatically within the compiler (I believe - I'll check, but the bound properties I believe are flagged by the IBindableVariableDefinition interface).
    
    I think this second approach is actually going to be needed, rather than just trying to use the decorated names everywhere, because of the potential issues with people trying to add listeners for changes in their bound private variables. Maybe that's rare, but #2 does seem a simple and more optimal approach..
    
    
    thanks
    
       Andrew
    
    
    -----Original Message-----
    From: Alex Harui [mailto:aharui@adobe.com.INVALID] 
    Sent: 06 December 2018 21:15
    To: dev@royale.apache.org
    Subject: [EXTERNAL] Re: Binding to private variables
    
    [Bindable]
    Private/public/protected var someVar;
    
    Always results in generated code to convert the var into a getters/setter so we can detect writes.
    
    A JS setter results in:
    
    Object.defineProperty("someVar",...   // if private it would be "MyClass_someVar"
    
    And that prevents the getter/setter from being renamed.
    
    I will note that if we generate decorated names for private variables we are generating a longer string in the final output that will not be minified.  This is will make binding to private variables more expensive than binding to public variables.  And what are the odds that the classes that use private binding are subclassing or will be subclassed with conflicting private names?  I'd guess that it would be close to zero.  So maybe the cheap answer is to tell folks to turn off private variable name decoration in the classes that want to use private binding.  Then our old code-gen will work.
    
    HTH,
    -Alex
    
    On 12/6/18, 12:52 PM, "Greg Dove" <gr...@gmail.com> wrote:
    
        I did a sweep on some of the less common bindings a couple of years ago.
        I think a few things that were working then may no longer be working after
        various changes. I need to check the test project for that.
        I'm happy to revisit that if people send me their binding bugs (better
        still add an issue wtih example and assign to me).
        But I want to get some other things in first, so probably won't get to that
        until end of next week.
        
        -Greg
        
        
        On Fri, Dec 7, 2018 at 9:43 AM Harbs <ha...@gmail.com> wrote:
        
        > What I’m not sure is how any of this worked in minified code.
        >
        > I’ve used [Bindable]private quite a lot…
        >
        > I’m not sure how to best fix this, but it does seem like a problem which
        > needs fixing.
        >
        > > On Dec 6, 2018, at 9:59 PM, Greg Dove <gr...@gmail.com> wrote:
        > >
        > > The other thing to consider is the ValueChangeEvent  that is being
        > > dispatched for automatically generated private [Bindable] vars
        > >
        > > It keeps the undecorated name in terms of what it dispatches.
        > >
        > > so for
        > > [Bindable]
        > > private var test:String
        > >
        > > you get
        > >
        > this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
        > >         this, "test", oldValue, value));
        > >
        > > in the generated setter
        > > but the test with the local name is
        > > if (value != this.com_foo_bar_test_)
        > > or similar
        > >
        > > I did not use private bindings very often. I wonder what would happen in
        > > legacy flex code if a parent class had a bindable private var and a
        > > subclass had a bindable private var with the same name. I expect they
        > both
        > > might dispatch the same property change events. And of course they could
        > be
        > > completely different types. I'm not going to test this :).
        > >
        > > The point is that I assume the dispatched event should still have the
        > > original (undecorated) name. So maybe this is more tricky and should
        > always
        > > be with a getter/setter like Alex suggests.
        > >
        > > Just sounding off some quick thoughts. I have not thought too deeply
        > about
        > > this, I am sure you will!
        > >
        > >
        > >
        > >
        > >
        > >
        > >
        > >
        > > On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:
        > >
        > >> Hi Andrew, I just had a quick go at doing this locally. I *think* it
        > >> should be able to be done in MXMLRoyaleEmitter with minor changes.
        > >>
        > >> I have not tested extensively, so I will just describe what I did and
        > you
        > >> can see if it works, and if it is universally applicable.
        > >>
        > >> The following is what I did:
        > >>
        > >> change the
        > >> encodeWatcher method signature to this:
        > >> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
        > >> sourceDefinition)
        > >>
        > >> and make 2 changes inside that method:
        > >>
        > >> #1.
        > >> for the part that is:
        > >> writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
        > >> propertyWatcherInfo.getPropertyName() +
        > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
        > >> ASEmitterTokens.COMMA.getToken());
        > >>
        > >> replace with this:
        > >>
        > >>            String outputName = propertyWatcherInfo.getPropertyName();
        > >>            if (sourceDefinition != null && sourceDefinition.isPrivate()
        > >> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
        > >> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
        > >> getMXMLWalker())
        > >> .getASEmitter();
        > >> outputName =
        > >> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
        > >> outputName);
        > >> }
        > >>            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
        > >> outputName +
        > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
        > >> ASEmitterTokens.COMMA.getToken());
        > >>
        > >>
        > >> #2
        > >> further down, comply with the new method signature:
        > >> replace:
        > >> encodeWatcher(ent.getValue());
        > >>
        > >> with:
        > >> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
        > >>                encodeWatcher(ent.getValue(), childSourceDefinition);
        > >>
        > >>
        > >>
        > >> And also in
        > >> outputBindingInfoAsData method:
        > >> comply with the new method signature for encodeWatcher
        > >>
        > >>  IDefinition sourceDefinition = (IDefinition) entry.getKey();
        > >>                encodeWatcher(watcherInfoBase, sourceDefinition);
        > >>
        > >>
        > >> I think that might work for the general case, and maybe it can be
        > >> simplified. I only tested quickly and just looked at the output, did not
        > >> actually try the resulting app. If this does not work, it might give
        > you a
        > >> lead.
        > >> -Greg
        > >>
        > >> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
        > >> wrote:
        > >>
        > >>> 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" <An...@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
        > >>>
        > >>>
        > >>>
        > >>>
        > >>>
        > >>>
        >
        >
        
    
    


RE: Binding to private variables

Posted by "Frost, Andrew" <An...@Harman.com>.
Hi all

Thanks for the inputs.. just getting round to going through this.

So I think we have two different approaches:

1) Ensure we correctly generate the property name to be used in the Watcher object.
This would be a fix in the emitter code as per Greg's suggestions, so that the Watcher would be correctly checking for a valueChange event coming from the correct property.
Greg you mentioned about the ValueChangeEvent being dispatched with the undecorated name of the property, but that's still fine as we're not listening out for that at this particular point: instead we're listening to changes that are being made on the object that's assigned to this property. However, I'm not sure what would happen if that property starts off as undefined/null and is then assigned at a later point, as in that case the watcher should be adding a listener to the parent object to spot when the property is set.. ah, having just checked it, yes you're right, this higher-level assignment is missed unless the property setter's event is also updated to the decorated property name...
So the changes required are:
a) in our list of bindings, we need to use the longer name
b) in our dispatching of ValueChangeEvents, we need to use the longer name
Although this (b) then also means we can't manually write code to listen to the changes, it only works within the binding framework..

2) For bindable variables, don't use the decorated name for private variables.
This is roughly what Alex suggests due to the longer strings that would then be generated throughout the binding code..
My slight amendment to this is that we can do it automatically within the compiler (I believe - I'll check, but the bound properties I believe are flagged by the IBindableVariableDefinition interface).

I think this second approach is actually going to be needed, rather than just trying to use the decorated names everywhere, because of the potential issues with people trying to add listeners for changes in their bound private variables. Maybe that's rare, but #2 does seem a simple and more optimal approach..


thanks

   Andrew


-----Original Message-----
From: Alex Harui [mailto:aharui@adobe.com.INVALID] 
Sent: 06 December 2018 21:15
To: dev@royale.apache.org
Subject: [EXTERNAL] Re: Binding to private variables

[Bindable]
Private/public/protected var someVar;

Always results in generated code to convert the var into a getters/setter so we can detect writes.

A JS setter results in:

Object.defineProperty("someVar",...   // if private it would be "MyClass_someVar"

And that prevents the getter/setter from being renamed.

I will note that if we generate decorated names for private variables we are generating a longer string in the final output that will not be minified.  This is will make binding to private variables more expensive than binding to public variables.  And what are the odds that the classes that use private binding are subclassing or will be subclassed with conflicting private names?  I'd guess that it would be close to zero.  So maybe the cheap answer is to tell folks to turn off private variable name decoration in the classes that want to use private binding.  Then our old code-gen will work.

HTH,
-Alex

On 12/6/18, 12:52 PM, "Greg Dove" <gr...@gmail.com> wrote:

    I did a sweep on some of the less common bindings a couple of years ago.
    I think a few things that were working then may no longer be working after
    various changes. I need to check the test project for that.
    I'm happy to revisit that if people send me their binding bugs (better
    still add an issue wtih example and assign to me).
    But I want to get some other things in first, so probably won't get to that
    until end of next week.
    
    -Greg
    
    
    On Fri, Dec 7, 2018 at 9:43 AM Harbs <ha...@gmail.com> wrote:
    
    > What I’m not sure is how any of this worked in minified code.
    >
    > I’ve used [Bindable]private quite a lot…
    >
    > I’m not sure how to best fix this, but it does seem like a problem which
    > needs fixing.
    >
    > > On Dec 6, 2018, at 9:59 PM, Greg Dove <gr...@gmail.com> wrote:
    > >
    > > The other thing to consider is the ValueChangeEvent  that is being
    > > dispatched for automatically generated private [Bindable] vars
    > >
    > > It keeps the undecorated name in terms of what it dispatches.
    > >
    > > so for
    > > [Bindable]
    > > private var test:String
    > >
    > > you get
    > >
    > this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
    > >         this, "test", oldValue, value));
    > >
    > > in the generated setter
    > > but the test with the local name is
    > > if (value != this.com_foo_bar_test_)
    > > or similar
    > >
    > > I did not use private bindings very often. I wonder what would happen in
    > > legacy flex code if a parent class had a bindable private var and a
    > > subclass had a bindable private var with the same name. I expect they
    > both
    > > might dispatch the same property change events. And of course they could
    > be
    > > completely different types. I'm not going to test this :).
    > >
    > > The point is that I assume the dispatched event should still have the
    > > original (undecorated) name. So maybe this is more tricky and should
    > always
    > > be with a getter/setter like Alex suggests.
    > >
    > > Just sounding off some quick thoughts. I have not thought too deeply
    > about
    > > this, I am sure you will!
    > >
    > >
    > >
    > >
    > >
    > >
    > >
    > >
    > > On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:
    > >
    > >> Hi Andrew, I just had a quick go at doing this locally. I *think* it
    > >> should be able to be done in MXMLRoyaleEmitter with minor changes.
    > >>
    > >> I have not tested extensively, so I will just describe what I did and
    > you
    > >> can see if it works, and if it is universally applicable.
    > >>
    > >> The following is what I did:
    > >>
    > >> change the
    > >> encodeWatcher method signature to this:
    > >> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
    > >> sourceDefinition)
    > >>
    > >> and make 2 changes inside that method:
    > >>
    > >> #1.
    > >> for the part that is:
    > >> writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> propertyWatcherInfo.getPropertyName() +
    > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> ASEmitterTokens.COMMA.getToken());
    > >>
    > >> replace with this:
    > >>
    > >>            String outputName = propertyWatcherInfo.getPropertyName();
    > >>            if (sourceDefinition != null && sourceDefinition.isPrivate()
    > >> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
    > >> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
    > >> getMXMLWalker())
    > >> .getASEmitter();
    > >> outputName =
    > >> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
    > >> outputName);
    > >> }
    > >>            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> outputName +
    > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> ASEmitterTokens.COMMA.getToken());
    > >>
    > >>
    > >> #2
    > >> further down, comply with the new method signature:
    > >> replace:
    > >> encodeWatcher(ent.getValue());
    > >>
    > >> with:
    > >> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
    > >>                encodeWatcher(ent.getValue(), childSourceDefinition);
    > >>
    > >>
    > >>
    > >> And also in
    > >> outputBindingInfoAsData method:
    > >> comply with the new method signature for encodeWatcher
    > >>
    > >>  IDefinition sourceDefinition = (IDefinition) entry.getKey();
    > >>                encodeWatcher(watcherInfoBase, sourceDefinition);
    > >>
    > >>
    > >> I think that might work for the general case, and maybe it can be
    > >> simplified. I only tested quickly and just looked at the output, did not
    > >> actually try the resulting app. If this does not work, it might give
    > you a
    > >> lead.
    > >> -Greg
    > >>
    > >> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
    > >> wrote:
    > >>
    > >>> 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" <An...@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
    > >>>
    > >>>
    > >>>
    > >>>
    > >>>
    > >>>
    >
    >
    


Re: Binding to private variables

Posted by Alex Harui <ah...@adobe.com.INVALID>.
[Bindable]
Private/public/protected var someVar;

Always results in generated code to convert the var into a getters/setter so we can detect writes.

A JS setter results in:

Object.defineProperty("someVar",...   // if private it would be "MyClass_someVar"

And that prevents the getter/setter from being renamed.

I will note that if we generate decorated names for private variables we are generating a longer string in the final output that will not be minified.  This is will make binding to private variables more expensive than binding to public variables.  And what are the odds that the classes that use private binding are subclassing or will be subclassed with conflicting private names?  I'd guess that it would be close to zero.  So maybe the cheap answer is to tell folks to turn off private variable name decoration in the classes that want to use private binding.  Then our old code-gen will work.

HTH,
-Alex

On 12/6/18, 12:52 PM, "Greg Dove" <gr...@gmail.com> wrote:

    I did a sweep on some of the less common bindings a couple of years ago.
    I think a few things that were working then may no longer be working after
    various changes. I need to check the test project for that.
    I'm happy to revisit that if people send me their binding bugs (better
    still add an issue wtih example and assign to me).
    But I want to get some other things in first, so probably won't get to that
    until end of next week.
    
    -Greg
    
    
    On Fri, Dec 7, 2018 at 9:43 AM Harbs <ha...@gmail.com> wrote:
    
    > What I’m not sure is how any of this worked in minified code.
    >
    > I’ve used [Bindable]private quite a lot…
    >
    > I’m not sure how to best fix this, but it does seem like a problem which
    > needs fixing.
    >
    > > On Dec 6, 2018, at 9:59 PM, Greg Dove <gr...@gmail.com> wrote:
    > >
    > > The other thing to consider is the ValueChangeEvent  that is being
    > > dispatched for automatically generated private [Bindable] vars
    > >
    > > It keeps the undecorated name in terms of what it dispatches.
    > >
    > > so for
    > > [Bindable]
    > > private var test:String
    > >
    > > you get
    > >
    > this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
    > >         this, "test", oldValue, value));
    > >
    > > in the generated setter
    > > but the test with the local name is
    > > if (value != this.com_foo_bar_test_)
    > > or similar
    > >
    > > I did not use private bindings very often. I wonder what would happen in
    > > legacy flex code if a parent class had a bindable private var and a
    > > subclass had a bindable private var with the same name. I expect they
    > both
    > > might dispatch the same property change events. And of course they could
    > be
    > > completely different types. I'm not going to test this :).
    > >
    > > The point is that I assume the dispatched event should still have the
    > > original (undecorated) name. So maybe this is more tricky and should
    > always
    > > be with a getter/setter like Alex suggests.
    > >
    > > Just sounding off some quick thoughts. I have not thought too deeply
    > about
    > > this, I am sure you will!
    > >
    > >
    > >
    > >
    > >
    > >
    > >
    > >
    > > On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:
    > >
    > >> Hi Andrew, I just had a quick go at doing this locally. I *think* it
    > >> should be able to be done in MXMLRoyaleEmitter with minor changes.
    > >>
    > >> I have not tested extensively, so I will just describe what I did and
    > you
    > >> can see if it works, and if it is universally applicable.
    > >>
    > >> The following is what I did:
    > >>
    > >> change the
    > >> encodeWatcher method signature to this:
    > >> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
    > >> sourceDefinition)
    > >>
    > >> and make 2 changes inside that method:
    > >>
    > >> #1.
    > >> for the part that is:
    > >> writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> propertyWatcherInfo.getPropertyName() +
    > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> ASEmitterTokens.COMMA.getToken());
    > >>
    > >> replace with this:
    > >>
    > >>            String outputName = propertyWatcherInfo.getPropertyName();
    > >>            if (sourceDefinition != null && sourceDefinition.isPrivate()
    > >> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
    > >> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
    > >> getMXMLWalker())
    > >> .getASEmitter();
    > >> outputName =
    > >> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
    > >> outputName);
    > >> }
    > >>            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> outputName +
    > >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
    > >> ASEmitterTokens.COMMA.getToken());
    > >>
    > >>
    > >> #2
    > >> further down, comply with the new method signature:
    > >> replace:
    > >> encodeWatcher(ent.getValue());
    > >>
    > >> with:
    > >> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
    > >>                encodeWatcher(ent.getValue(), childSourceDefinition);
    > >>
    > >>
    > >>
    > >> And also in
    > >> outputBindingInfoAsData method:
    > >> comply with the new method signature for encodeWatcher
    > >>
    > >>  IDefinition sourceDefinition = (IDefinition) entry.getKey();
    > >>                encodeWatcher(watcherInfoBase, sourceDefinition);
    > >>
    > >>
    > >> I think that might work for the general case, and maybe it can be
    > >> simplified. I only tested quickly and just looked at the output, did not
    > >> actually try the resulting app. If this does not work, it might give
    > you a
    > >> lead.
    > >> -Greg
    > >>
    > >> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
    > >> wrote:
    > >>
    > >>> 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" <An...@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
    > >>>
    > >>>
    > >>>
    > >>>
    > >>>
    > >>>
    >
    >
    


Re: Binding to private variables

Posted by Greg Dove <gr...@gmail.com>.
I did a sweep on some of the less common bindings a couple of years ago.
I think a few things that were working then may no longer be working after
various changes. I need to check the test project for that.
I'm happy to revisit that if people send me their binding bugs (better
still add an issue wtih example and assign to me).
But I want to get some other things in first, so probably won't get to that
until end of next week.

-Greg


On Fri, Dec 7, 2018 at 9:43 AM Harbs <ha...@gmail.com> wrote:

> What I’m not sure is how any of this worked in minified code.
>
> I’ve used [Bindable]private quite a lot…
>
> I’m not sure how to best fix this, but it does seem like a problem which
> needs fixing.
>
> > On Dec 6, 2018, at 9:59 PM, Greg Dove <gr...@gmail.com> wrote:
> >
> > The other thing to consider is the ValueChangeEvent  that is being
> > dispatched for automatically generated private [Bindable] vars
> >
> > It keeps the undecorated name in terms of what it dispatches.
> >
> > so for
> > [Bindable]
> > private var test:String
> >
> > you get
> >
> this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
> >         this, "test", oldValue, value));
> >
> > in the generated setter
> > but the test with the local name is
> > if (value != this.com_foo_bar_test_)
> > or similar
> >
> > I did not use private bindings very often. I wonder what would happen in
> > legacy flex code if a parent class had a bindable private var and a
> > subclass had a bindable private var with the same name. I expect they
> both
> > might dispatch the same property change events. And of course they could
> be
> > completely different types. I'm not going to test this :).
> >
> > The point is that I assume the dispatched event should still have the
> > original (undecorated) name. So maybe this is more tricky and should
> always
> > be with a getter/setter like Alex suggests.
> >
> > Just sounding off some quick thoughts. I have not thought too deeply
> about
> > this, I am sure you will!
> >
> >
> >
> >
> >
> >
> >
> >
> > On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:
> >
> >> Hi Andrew, I just had a quick go at doing this locally. I *think* it
> >> should be able to be done in MXMLRoyaleEmitter with minor changes.
> >>
> >> I have not tested extensively, so I will just describe what I did and
> you
> >> can see if it works, and if it is universally applicable.
> >>
> >> The following is what I did:
> >>
> >> change the
> >> encodeWatcher method signature to this:
> >> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
> >> sourceDefinition)
> >>
> >> and make 2 changes inside that method:
> >>
> >> #1.
> >> for the part that is:
> >> writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> >> propertyWatcherInfo.getPropertyName() +
> >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> >> ASEmitterTokens.COMMA.getToken());
> >>
> >> replace with this:
> >>
> >>            String outputName = propertyWatcherInfo.getPropertyName();
> >>            if (sourceDefinition != null && sourceDefinition.isPrivate()
> >> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
> >> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
> >> getMXMLWalker())
> >> .getASEmitter();
> >> outputName =
> >> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
> >> outputName);
> >> }
> >>            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> >> outputName +
> >>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> >> ASEmitterTokens.COMMA.getToken());
> >>
> >>
> >> #2
> >> further down, comply with the new method signature:
> >> replace:
> >> encodeWatcher(ent.getValue());
> >>
> >> with:
> >> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
> >>                encodeWatcher(ent.getValue(), childSourceDefinition);
> >>
> >>
> >>
> >> And also in
> >> outputBindingInfoAsData method:
> >> comply with the new method signature for encodeWatcher
> >>
> >>  IDefinition sourceDefinition = (IDefinition) entry.getKey();
> >>                encodeWatcher(watcherInfoBase, sourceDefinition);
> >>
> >>
> >> I think that might work for the general case, and maybe it can be
> >> simplified. I only tested quickly and just looked at the output, did not
> >> actually try the resulting app. If this does not work, it might give
> you a
> >> lead.
> >> -Greg
> >>
> >> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
> >> wrote:
> >>
> >>> 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" <An...@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
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
>
>

Re: Binding to private variables

Posted by Harbs <ha...@gmail.com>.
What I’m not sure is how any of this worked in minified code.

I’ve used [Bindable]private quite a lot…

I’m not sure how to best fix this, but it does seem like a problem which needs fixing.

> On Dec 6, 2018, at 9:59 PM, Greg Dove <gr...@gmail.com> wrote:
> 
> The other thing to consider is the ValueChangeEvent  that is being
> dispatched for automatically generated private [Bindable] vars
> 
> It keeps the undecorated name in terms of what it dispatches.
> 
> so for
> [Bindable]
> private var test:String
> 
> you get
> this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
>         this, "test", oldValue, value));
> 
> in the generated setter
> but the test with the local name is
> if (value != this.com_foo_bar_test_)
> or similar
> 
> I did not use private bindings very often. I wonder what would happen in
> legacy flex code if a parent class had a bindable private var and a
> subclass had a bindable private var with the same name. I expect they both
> might dispatch the same property change events. And of course they could be
> completely different types. I'm not going to test this :).
> 
> The point is that I assume the dispatched event should still have the
> original (undecorated) name. So maybe this is more tricky and should always
> be with a getter/setter like Alex suggests.
> 
> Just sounding off some quick thoughts. I have not thought too deeply about
> this, I am sure you will!
> 
> 
> 
> 
> 
> 
> 
> 
> On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:
> 
>> Hi Andrew, I just had a quick go at doing this locally. I *think* it
>> should be able to be done in MXMLRoyaleEmitter with minor changes.
>> 
>> I have not tested extensively, so I will just describe what I did and you
>> can see if it works, and if it is universally applicable.
>> 
>> The following is what I did:
>> 
>> change the
>> encodeWatcher method signature to this:
>> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
>> sourceDefinition)
>> 
>> and make 2 changes inside that method:
>> 
>> #1.
>> for the part that is:
>> writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
>> propertyWatcherInfo.getPropertyName() +
>>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
>> ASEmitterTokens.COMMA.getToken());
>> 
>> replace with this:
>> 
>>            String outputName = propertyWatcherInfo.getPropertyName();
>>            if (sourceDefinition != null && sourceDefinition.isPrivate()
>> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
>> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
>> getMXMLWalker())
>> .getASEmitter();
>> outputName =
>> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
>> outputName);
>> }
>>            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
>> outputName +
>>                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
>> ASEmitterTokens.COMMA.getToken());
>> 
>> 
>> #2
>> further down, comply with the new method signature:
>> replace:
>> encodeWatcher(ent.getValue());
>> 
>> with:
>> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
>>                encodeWatcher(ent.getValue(), childSourceDefinition);
>> 
>> 
>> 
>> And also in
>> outputBindingInfoAsData method:
>> comply with the new method signature for encodeWatcher
>> 
>>  IDefinition sourceDefinition = (IDefinition) entry.getKey();
>>                encodeWatcher(watcherInfoBase, sourceDefinition);
>> 
>> 
>> I think that might work for the general case, and maybe it can be
>> simplified. I only tested quickly and just looked at the output, did not
>> actually try the resulting app. If this does not work, it might give you a
>> lead.
>> -Greg
>> 
>> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
>> wrote:
>> 
>>> 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" <An...@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
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 


Re: Binding to private variables

Posted by Greg Dove <gr...@gmail.com>.
The other thing to consider is the ValueChangeEvent  that is being
dispatched for automatically generated private [Bindable] vars

It keeps the undecorated name in terms of what it dispatches.

so for
[Bindable]
private var test:String

you get
 this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(
         this, "test", oldValue, value));

in the generated setter
but the test with the local name is
if (value != this.com_foo_bar_test_)
 or similar

I did not use private bindings very often. I wonder what would happen in
legacy flex code if a parent class had a bindable private var and a
subclass had a bindable private var with the same name. I expect they both
might dispatch the same property change events. And of course they could be
completely different types. I'm not going to test this :).

The point is that I assume the dispatched event should still have the
original (undecorated) name. So maybe this is more tricky and should always
be with a getter/setter like Alex suggests.

Just sounding off some quick thoughts. I have not thought too deeply about
this, I am sure you will!








On Fri, Dec 7, 2018 at 8:16 AM Greg Dove <gr...@gmail.com> wrote:

> Hi Andrew, I just had a quick go at doing this locally. I *think* it
> should be able to be done in MXMLRoyaleEmitter with minor changes.
>
> I have not tested extensively, so I will just describe what I did and you
> can see if it works, and if it is universally applicable.
>
> The following is what I did:
>
> change the
> encodeWatcher method signature to this:
> private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
> sourceDefinition)
>
> and make 2 changes inside that method:
>
> #1.
> for the part that is:
>  writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> propertyWatcherInfo.getPropertyName() +
>                     ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> ASEmitterTokens.COMMA.getToken());
>
> replace with this:
>
>             String outputName = propertyWatcherInfo.getPropertyName();
>             if (sourceDefinition != null && sourceDefinition.isPrivate()
> && getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
> JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker)
> getMXMLWalker())
> .getASEmitter();
> outputName =
> fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
> outputName);
> }
>             writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> outputName +
>                     ASEmitterTokens.DOUBLE_QUOTE.getToken() +
> ASEmitterTokens.COMMA.getToken());
>
>
> #2
> further down, comply with the new method signature:
> replace:
>  encodeWatcher(ent.getValue());
>
> with:
> IDefinition childSourceDefinition = (IDefinition) ent.getKey();
>                 encodeWatcher(ent.getValue(), childSourceDefinition);
>
>
>
> And also in
> outputBindingInfoAsData method:
> comply with the new method signature for encodeWatcher
>
>   IDefinition sourceDefinition = (IDefinition) entry.getKey();
>                 encodeWatcher(watcherInfoBase, sourceDefinition);
>
>
> I think that might work for the general case, and maybe it can be
> simplified. I only tested quickly and just looked at the output, did not
> actually try the resulting app. If this does not work, it might give you a
> lead.
> -Greg
>
> On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid>
> wrote:
>
>> 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" <An...@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
>>
>>
>>
>>
>>
>>

Re: Binding to private variables

Posted by Greg Dove <gr...@gmail.com>.
Hi Andrew, I just had a quick go at doing this locally. I *think* it should
be able to be done in MXMLRoyaleEmitter with minor changes.

I have not tested extensively, so I will just describe what I did and you
can see if it works, and if it is universally applicable.

The following is what I did:

change the
encodeWatcher method signature to this:
private void encodeWatcher(WatcherInfoBase watcherInfoBase, IDefinition
sourceDefinition)

and make 2 changes inside that method:

#1.
for the part that is:
 writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
propertyWatcherInfo.getPropertyName() +
                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
ASEmitterTokens.COMMA.getToken());

replace with this:

            String outputName = propertyWatcherInfo.getPropertyName();
            if (sourceDefinition != null && sourceDefinition.isPrivate() &&
getMXMLWalker().getProject().getAllowPrivateNameConflicts()) {
JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
outputName =
fjs.formatPrivateName(sourceDefinition.getParent().getQualifiedName(),
outputName);
}
            writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() +
outputName +
                    ASEmitterTokens.DOUBLE_QUOTE.getToken() +
ASEmitterTokens.COMMA.getToken());


#2
further down, comply with the new method signature:
replace:
 encodeWatcher(ent.getValue());

with:
IDefinition childSourceDefinition = (IDefinition) ent.getKey();
                encodeWatcher(ent.getValue(), childSourceDefinition);



And also in
outputBindingInfoAsData method:
comply with the new method signature for encodeWatcher

  IDefinition sourceDefinition = (IDefinition) entry.getKey();
                encodeWatcher(watcherInfoBase, sourceDefinition);


I think that might work for the general case, and maybe it can be
simplified. I only tested quickly and just looked at the output, did not
actually try the resulting app. If this does not work, it might give you a
lead.
-Greg

On Fri, Dec 7, 2018 at 7:58 AM Alex Harui <ah...@adobe.com.invalid> wrote:

> 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" <An...@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
>
>
>
>
>
>

Re: Binding to private variables

Posted by Alex Harui <ah...@adobe.com.INVALID>.
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" <An...@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