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

Reply via email to