Let's say I want to load an FXML document to be used somewhere in my application. As far as I'm aware, there are two ways of doing this:
Call the static FXMLLoader#load(<various resource args>) method.
Initialize an FXMLLoader (with the resource location), and then call load() on that instance.
My question is what exactly "loading" an FXML document does here.
Initially, I assumed the static method would do an entire parse "cycle" on every call, and that creating an instance would allow multiple loads to take advantage of some kind of preprocessed representation, but documentation for the non-static load() method just states;
"Loads an object hierarchy from an FXML document. The location from which the document will be loaded...", which sounds like the document is loaded on every call.
I'm using JavaFX 17.
After spending a fair bit of time with the source, I feel I can give a pretty good overview of how FXML loading functions behind the scenes. That being said, I can't guarantee that there isn't anything I didn't miss. I've thoroughly looked over quite a bit of code I thought to be important, but most isn't all, and I may have simply not noticed something.
This answer should be valid for JavaFX 17.
As a TLDR answering the main concern of my question: As far as I can tell, no information is cached across load() calls, regardless of whether you use the static or non-static versions. That being said, the non-static calls will still give you a slight performance gain, the fastest of which is the load(InputStream inputStream) overload, which (in addition to skipping some argument processing) will prevent the loader from opening a new InputStream on every call.
I've built a call graph (CallGraph Viewer) showing important parts of the FXML loading code in order to make it a bit more digestible.
This is easily the most likely part of my answer to contain inaccuracies. To generate this graph, I simply copied the FXMLLoader code into eclipse and generated connections for parts of the code I deemed important. Unfortunately, the plugin doesn't always correctly parse code containing missing imports, requiring me to write in definitions for a couple of classes, but I left most alone. Additionally, the initial result was incomprehensible and needed a fair bit of manual cleanup, a large portion of which was done simply based on whether I thought something sounded useful or not.
If you are unfamiliar with eclipse's icons, documentation can be found here (make sure to zoom the image, or open it in a new tab, or I doubt you will be able to see much).
Yes, there are three processEndElement() methods with the same signature, they are overridden methods in subclasses of Element.
If you're wondering what I spent all that manual cleanup time on, try not to worry about the individual methods, more the overall structure.
Here's my breakdown of this mess as a step by step recreation of what happens when load() is called:
The application calls one of the public load() methods. This simply calls a matching loadImpl() overload (static if the load() call was static and vice-versa) with the provided arguments. All existing loadImpl() overloads also ask for the class which called them, which the method attempts to provide with a java.lang.StackWalker. No additional processing is done.
After passing the public interface, execution is routed through a hierarchy of loadImpl() calls. Each overload just calls an overload with one more argument than itself, passing on its own arguments and giving null for the missing one (except in the case of a missing charset, which is given a default value).
The more arguments you give to load(), the farther you start in the hierarchy, with non-static versions beginning after the static ones. If you call one of the static overloads, an instance of the FXMLLoader class is created at the final static loadImpl(), which is used to continue onto the non-static calls.
Once reaching the non-static loadImpl() calls, things begin to get interesting. If using the load(void) overload, an InputStream is created based on arguments set when the FXMLLoader instance was initialized, and is given to the next stage in the hierarchy as before. At the final (non-static) loadImpl() (which can be called immediately using the load(InputStream inputStream) overload; this is the fastest method I know of to get from the initial load() call to XML processing), we finally exit the loadImpl() hierarchy, and move to XML processing.
Two things happen here:
a ControllerAccessor instance is given the callingClass argument passed up the loadImpl() hierarchy. I can't exactly explain how this class works, but it contains two Map's; controllerFields and controllerMethods, used in the initialization of controllers.
clearImports() is called, clearing packages (a List) and classes (a Map), both used in further XML processing.
The four variables here (except for maybe the controller ones, I'm a little iffy on them) act as important cache data for the backend XML processing cycle. However, all are cleared between loads (there is no logic controlling their execution, if the load succeeded, the cache data will not have survived), so using an FXMLLoader instance will not improve performance due to data caching (it's still worth using one, however, as the non-static calls skip much of the loadImpl() hierarchy, and you can even reuse the InputStream if using that particular overload).
Next, the XML parser itself is loaded. First, a new instance of a XMLInputFactory is created. This is then used to create a XmlStreamReader from the provided InputStream
Finally, we now begin actually processing the loaded XML.
The main XML processing loop is actually relatively simple to explain;
First, the code enters a while loop, checking the value of xmlStreamReader.hasNext().
During each cycle, a switch statement is entered, routing execution to different process<X>() methods depending on what the XML reader encounters. Those methods process the incoming events, and use an assortment of more "backend" methods to carry out common operations (The 'backend XML processing' section of the call graph is only a small portion of the actual code). These include methods like processImports(), which calls importPackage() or importClass(), in turn populating the packages and classes caches. Those caches are accessed by getType(), a backend method used by many other processing methods.
Additionally, I think that some part of controllers is "assigned" during this stage; processEndElements(), for example, ends up calling getControllerFields() or getControllerMethods(), which access the aforementioned controllerFields and controllerMethods caches, but also sometimes modify them. That being said, the call graph gets a bit too deep for me to easily understand at this point, and those methods are also called later, so I can't be sure.
After XML processing, a controller (controllers? see comment below) is initialized. You can read about controller initialization a bit in James_D's answer here, but I don't have much to say about it, as this is the section I am least confident in understanding.
That being said, it is interesting to note that this code is out of the previous while loop; only one initialization method is called. Either what seems like one call is actually multiple (which is definitely possible; the initialization "method" called is returned by controllerAccessor.getControllerMethods() and "it" is called using the MethodHelper JavaFX class), or only one controller is initialized here (assumedly the controller for the root node) and the others are initialized during parsing. I'd lean towards the first possibility here, but that's based purely on intuition.
Finally (and if you're still reading by now, consider me impressed), we enter cleanup. This stage is super simple;
The ControllerAccessor has its "calling class" variable nulled, and its controllerFields and controllerMethods caches cleared.
The XmlStreamReader instance is nulled.
The root node is returned, and thus the function exits.
Thanks to #jewelsea for links to other answers and for recommending I look at the source.
Related
I'm modifying a code written in c++ in order to add several features required by my company. I need to modify as less as possible this code, because it's a public code get from a Git repository, and we want to avoid to deviate from the original source code in case we need to synchronize our code with possible new versions in the future.
In this code, a structure is initialized with a call to std::memset. And I had need to add a shared pointer to this structure.
I notice no issue about that, the code compiles, links and works as expected, and I get even no warnings while the compilation.
But is it safe to achieve that this way? May a std::shared_ptr be correctly initialized if it is part of a structure initialized with std::memset? Or are side effects or hazardous issues which prevent to do that?
Basically, I want to be able to generate class definitions, compile the system, and save it for reuse. Would that involve a code walker, or is there a simpler option?
(save-lisp-and-die "isn't going to work for me")
Expanding to explain. I'm generating systems based on OpenAPI definitions, so a system roughly corresponds to an API client.
There will be dozens, if not hundreds of these.
The idea is to NOT keep them all in the image, but load at run time as required.
I see two possible routes here, and to some extent, I suspect they mainly differ in "the last mile" (as it were).
The route you seem to have settled on, run-time definition of classes and functions.
A route whereby you generate your function/class forms, but don't go the full way to get them "Live" in the image and instead emit the form(s) to a file.
I suspect that it would be possible to have most of the generating code shared between the two and for the first route have a wrapping macro that effectively returns a PROGN, and in the second calls a function to pretty-print what the macro would have returned on a stream.
Saying that, building a tailored environment and saving it to a "core" file is a pretty good way of getting excellent startup times.
VS2015 shows how many references there are to a class.
However if the class is started by WebApplication.Run the references are not shown.
The highlighted code does get executed when the application is run.
Is there any way to get the reference count for the Configure method?
Here are two reasons ;)
The Startup Class is invoked by reflection (it does not implement an interface)
I doubt that code pieces outside of your local source code will influence the reference count. So even if somewhere deep in WebApplication.Run the Configure method is invoked (assuming directly over some magic interface), the reference code will not rise. Make sense, otherwise the counter for string would have overflow ;)
Because I rush in development (a lot of whip cracking here) and declare my objects at the top of the function and instantiate inside my try-catch block, I get a lot of the good old "object not set to an instance of an object" errors while doing TDD, and later if I do miss a branch that object was used in (doing VB now, would prefer C#) or just in every day coding, object not set to an instance of an object is a bit vague. Sure the stack trace sends me to the line the error occured at, but it would be nice if I could modify my logging to either name the object or its type because sometimes I have multiple objects on the same line. It's not the end of the world, but in the end it would save me a few minutes each day. Any ideas on how I can pass the info on which object wasn't set? Thanks
It is non trivial to "modify your logging" to output variable name or type - I am sure that if the framework could easily get this information from the executing IL, MS would have included it the null reference exception.
Prevention is always better than cure. Here are a couple of tips I would do
Fix your compiler warnings
C# would generate a compile error if it detects that there are code paths that could use an unassigned local variable. [For some odd reason] VB.Net will still compile but the compiler will generate a warning - take heed of these and go and fix the code and you should never run into the problem of unassigned variables again!
Adopt a different coding pattern for variable declaration
I appreciate that method variable scope in ye olde VB was that the variable was visible throughout the entire method regardless of where it was defined. As a result, it was a reasonable practice to put all your var declarations at the top of the method. VB.Net of course is different - you can only use variables after they are declared and so it is OK (and I would say preferable*) to put the declaration (and assignment) closer to where the variable is actually used. This should help you see "by eye" if your program logic means it is possible to use an unassigned variable.
Some people think this is a think that it is always good practice to put variable declarations in a block at the top of the method. I will not argue against them but I would say that that approach works best with small methods that do not use lots of variables.
Over the past three weeks, I have lost at least 120 man hours because of some lesser known functionality in ActionScript 3. One of the problems was that, with Internet Explorer, if there are too many messages sent through LocalConnections, it will slow the messages sent through, but in the standalone player and in Firefox, this threshold is significantly higher. Another was that the static values of a class are instantiated even if the member itself is not being used:
import path.to.FooClass;
private function thisIsNeverCalledButItEnsuresThatFooClassIsImported():void
{
var f:FooClass = new FooClass();
}
Since FooClass had a static reference to a Singleton, that Singleton was instantiated so when I loaded a Module which used that Singleton, it would bind to values in an unpredictable way.
Additional cases where things behave in an unexpected way:
MovieClip.addFrameScript
flash.trace.Trace as a class
int is a faster incrementer class, Number is faster for mathematics, and uint is incredibly slow.
PrintDataGrid, if it has only one page, needs to have an empty value appended to the end of its dataProvider
If you use try...catch around two LocalConnections and connect them to the same channel, you will force garbage collection without needing System.gc
So here's the question (and I'm sorry for omitting this in the original post), is there any consolidated documentation for this type of behavior anywhere? Is there any (even non-Adobe) documentation on these issues (websites, forums, books, ANYTHING)? I know that these things are definitely easy enough TO document, but has anyone done so?
If not, am I missing anything? Are there other issues which I should know about?
This kind of useful information is very often not "centralized". Moreover, what you are looking for is something related to the experience of the programmer (more than to official docs).
FYI, there are two other methods for ensuring a class is included.
#1 - This syntax is actually used in the Flex source code:
import path.to.FooClass; FooClass; // note double reference
public class References
{
// No references needed in this class
}
#2 - Use the includes command line argument
-includes path.to.FooClass
You can always submit your experience using the "feedback" section in the help. Unfortunately, this is less obvious than the link that used to be at the bottom of each page in the older help files (which also served the useful function of opening a browser window with the web version of that help page).
Adobe says that it incorporates the comments from previous versions of the help into new versions, but my own observation suggests that there are instances where it does not happen. However, that and the appropriate cookbook are still the best avenue for those who believe that this kind of information should be centralized.
Note that the whole purpose behind modules is to avoid compiling code multiple times, so importing FooClass kind of defeated the purpose. The problems you had in this instance are just one of the many that happen if you use Singletons, and it's unfortunate that the first official Framework, Cairngorm, encouraged their widespread use. Check out http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/ .