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