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