For sure, there are probably still bugs in the remove-circulars calculation.
But, before debugging these problems, make sure you remove the entire
bin/js-debug folder. The JS files in there are modified by the dependency
management code and could get stale.
The fundamental problem is that goog.provides, goog.requires, and
goog.addDependency result in <script> tags being added at runtime (in debug
mode) to bring in the JS files needed by the classes. Similarly, the Google
Closure Compiler orders the classes in the minified output so that initializers
are ready to be used when needed.
The Closure Compiler used to complain when it detected a circularity (when a
goog.provides("A") has goog.requires("B") but goog.provides("B") has
goog.requires("A"). Maybe they turned that off in a recent version? Because I
would have expected the Closure Compiler to complain when Andrew set
remove-circulars=false.
ActionScript disallows certain kinds of circular dependencies as well, but many
are allowed in AS and are actually legal in JS that the Closure Compiler
complains about. So the whole goal of the GoogDepsWriter is to detect the
legal circularities and rewrite the output JS files to make Closure Compiler
happy. So we do that by putting lots of goog.requires in the main application
in 'the right order'. The GoogDepsWriter tries to detect certain kinds of
static initializers and overrides the ordering. I think the pattern Andrew is
using isn't currently detected. Harbs' suggestion of having the compiler
output static initializers differently might work, but also might just move the
problem.
Sometimes, a simple app exposes more of these issues that a complex one because
the complex one often has enough code that also uses one of the classes
involved that it modifies the order of goog.requires in the big list in the
main jS file.
So, IMO, the choices are:
1) just have folks workaround the issue using the pattern Harbs showed
2) output static initializers differently
3) do better detection of static initializers in GoogDepsWriter
Thoughts?
-Alex
On 7/19/18, 7:11 AM, "Harbs" <[email protected]> wrote:
Here’s the short version:
All the files are loaded in order of the dependency declarations.
Calculating the correct order is very difficult and sometimes impossible.
The way the circular dependencies are resolved is by simply declaring them
all in the main class. Declaring them in subclasses can cause goog to try and
load files before they are a actually loaded. The exact order ends up being a
bit of a crapshoot. Statics are evaluated when the file is first loaded, so
initializing anything other than native (or externally loaded) objects can
cause an error.
My proposed solution is to extract all non-native costs and initialized
vars into a separate file and load that file last in the dependency list. That
ensures all the classes are already loaded. Unfortunately no-one has worked on
resolving this problem yet. Earlier versions of the compiler tried to be
smarter about order, but that turned out to not always work…
Alex could give you the longer version… ;-)
What I do for cases where static custom classes are needed is something
like this:
private static var _myFoo:Foo;
public static function get FOO():Foo{
if(!_myFoo){
_myFoo = new Foo();
}
return _myFoo;
}
There’s slightly more overhead than a const, but at least it works…
Harbs
> On Jul 19, 2018, at 4:39 PM, Frost, Andrew <[email protected]>
wrote:
>
> Hi
>
>> If it’s in the big list, it should be initialized correctly.
> .. but depending on the order of the big list, things will be initialised
in different orders...
>
>> Are you initializing a BinaryData as a static const (or preinitialized
var)?
> Yes
>> i.e. public static const DATA:BinaryData = new BinaryData() is a no-no.
> So - why is this a no-no? I'm trying to convert an ActionScript class
that Adobe wrote a long time ago, which has the equivalent using ByteArray. Are
you saying that it's not possible to do certain things with ActionScript in
Royale that are fine when you're targeting Flash?
> I know it's not a great practice to have this sort of thing but I would
hope that there is some level of parity between the ActionScript code that you
can write for each of the targets; plus, as I've found, all that's needed for
this construct to work 100% of the time is if we could specify the correct
dependencies for the BinaryData class.
>
>> I’d really like to have the compiler delay loading of static consts and
var to the very end of the loading. That would allow this pattern…
> True, and possibly that could work, but surely it would also be allowed
if we generated the line:
> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js',
> ['org.apache.royale.utils.BinaryData'], [
'org.apache.royale.utils.Endian',
> 'org.apache.royale.utils.IBinaryDataInput',
'org.apache.royale.utils.IBinaryDataOutput']);
>
> So regardless of all the discussion, I would quite like to find out why
the 'Endian' isn't listed as a dependency when all this html/JS stuff is
generated... likewise some of the other constructs where something depends on
something else (e.g. "is" is being used in the Namespace constructor, but I'm
creating a Namespace prior to the Language.js file being loaded..)
>
> I guess we can't actually wait until the end of loading everything until
we start to initialise things, unless we change how the JS code is output (i.e.
to have a single static initialiser for the class which is run after everything
is loaded, rather than declaring all the static properties with initialisation
in-place..)
>
> Interesting problem!
>
> thanks
>
> Andrew
>
>
> -----Original Message-----
> From: Harbs [mailto:[email protected]]
> Sent: 19 July 2018 14:19
> To: [email protected]
> Subject: [EXTERNAL] Re: Query on Royale dependency generation
>
> If it’s in the big list, it should be initialized correctly.
>
> Are you initializing a BinaryData as a static const (or preinitialized
var)?
>
> i.e. public static const DATA:BinaryData = new BinaryData() is a no-no.
>
> I’d really like to have the compiler delay loading of static consts and
var to the very end of the loading. That would allow this pattern…
>
>> On Jul 19, 2018, at 4:06 PM, Frost, Andrew <[email protected]>
wrote:
>>
>> Hi
>>
>> Yes (it's weird!) - okay so I just set the -remove-circulars option to
true explicitly but still no change (from the default).
>>
>> If you look in your html file, do you get just:
>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js',
>> ['org.apache.royale.utils.BinaryData'],
>> ['org.apache.royale.utils.IBinaryDataInput',
>> 'org.apache.royale.utils.IBinaryDataOutput']);
>> for the dependencies of it?
>>
>> In the big list of dependencies at the start, then yes Endian is showing
up there. BinaryData isn't in that big list, because this is a dependency of
another of my classes (which is in the list). It's constructor is being called
because there's a static property being initialised i.e.
>> static private var _bytes : BinaryData = new BinaryData();
>>
>>
>> From a bit more digging, the issue (whether it happens or not) seems to
be due to the order in which the files are loaded; there can be a specific
order be applied in terms of ensuring that the dependencies are loaded before
the classes that require them, but there isn't a dependency link between
BinaryData and Endian (as you can see from your compiled .js files). So without
this dependency, it seems like luck? as to which order these go in?
>>
>> I've just created another project and this one works fine, but in the
network info I can that Endian.js is being loaded prior to BinaryData.js. I
can't see why this would be: it seems to just be down to the order that the big
dependency list at the start of the html script is written. And I can't
actually see why with one project, I'm ending up with one of our own classes
early on in that list ... which means I currently have two solutions for fixing
this: (a) add the endian dependency to the binarydata line in the html; (b)
reorder the main list of dependencies at the top of the html to make endian
appear earlier on. Neither of these are a good solution when this file is
generated on every build!!
>>
>> So: am I right in thinking that the dependency list for the
BinaryData.js should include the Endian one? i.e. this is wrong:
>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js',
>> ['org.apache.royale.utils.BinaryData'],
>> ['org.apache.royale.utils.IBinaryDataInput',
>> 'org.apache.royale.utils.IBinaryDataOutput']);
>>
>> If so, then that needs to change (somehow) and it would then solve my
problem...
>>
>> thanks
>>
>> Andrew
>>
>>
>> -----Original Message-----
>> From: Harbs [mailto:[email protected]]
>> Sent: 19 July 2018 13:32
>> To: [email protected]
>> Subject: [EXTERNAL] Re: Query on Royale dependency generation
>>
>> The Endian dependency should be added to the main application
dependency. The entire list should be included there. (I have 872 dependencies
listed.) ‘org.apache.royale.utils.Endian’ is one of them.
>>
>> I am also using URLStream and URLBinaryLoader which list Endian as a
dependency, so it’s possible it’s being pulled from there. I’m not sure I have
an application which uses BinaryData without one of those.
>>
>>> On Jul 19, 2018, at 3:19 PM, Harbs <[email protected]> wrote:
>>>
>>> That’s weird. I believe the default is false (although I think the
default should be true).
>>>
>>> Try setting -remove-circulars to true. I think that should resolve it.
>>>
>>> Yes. I’m using BinaryData extensively.
>>>
>>>> On Jul 19, 2018, at 3:15 PM, Frost, Andrew <[email protected]>
wrote:
>>>>
>>>> Hi
>>>>
>>>> I'm not using it on my build command line, i.e. it's set as the
default 'true'.
>>>>
>>>> If I do set it to 'false' then I get what I would expect from the
code, i.e. it ignores the interfaces and just outputs the discovered
dependencies - including the google one:
>>>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js',
>>>> ['org.apache.royale.utils.BinaryData'], ['goog.DEBUG',
>>>> 'org.apache.royale.utils.Endian']);
>>>>
>>>> But now I get hundreds of errors from the browser's JS engine e.g.
>>>> IStatesObject.js:42 Uncaught TypeError: Cannot read property
>>>> 'IEventDispatcher' of undefined at IStatesObject.js:42
>>>> (anonymous) @ IStatesObject.js:42
>>>> IUIBase.js:58 Uncaught TypeError: Cannot read property
>>>> 'IEventDispatcher' of undefined at IUIBase.js:58
>>>> (anonymous) @ IUIBase.js:58
>>>> IBeadModel.js:38 Uncaught TypeError: Cannot read property
>>>> 'IEventDispatcher' of undefined at IBeadModel.js:38
>>>> (anonymous) @ IBeadModel.js:38
>>>> Event.js:33 Uncaught TypeError: Cannot read property 'Event' of
>>>> undefined at Event.js:33
>>>> (anonymous) @ Event.js:33
>>>> base.js:2484 Uncaught TypeError: Cannot read property 'prototype' of
>>>> undefined at Object.goog.inherits (base.js:2484) at UIBase.js:44
>>>> goog.inherits @ base.js:2484
>>>> (anonymous) @ UIBase.js:44
>>>> base.js:2484 Uncaught TypeError: Cannot read property 'prototype' of
>>>> undefined at Object.goog.inherits (base.js:2484) at
>>>> eventtarget.js:96 goog.inherits @ base.js:2484
>>>> (anonymous) @ eventtarget.js:96
>>>> base.js:2484 Uncaught TypeError: Cannot read property 'prototype' of
>>>> undefined at Object.goog.inherits (base.js:2484) at
>>>> HTMLElementWrapper.js:28 .....
>>>>
>>>>
>>>> I'm assuming I'm not meant to be editing the html dependency list
manually (there's another one to change, Namespace.js has a dependency on
Language.js due to the use of "is") so unless something here is
project-specific that's changing how it's outputting/parsing the dependencies,
I'm not sure what's up.
>>>>
>>>> If anyone has a project that uses BinaryData, are they able to check
>>>> what they see in the generated HTML for that one, to see whether
>>>> it's just me who doesn't have the Endian dependency added..? FWIW
>>>> I've been trying both with 0.9.2 downloaded via NPM, and the latest
>>>> develop branch (well perhaps a week out of date now..)
>>>>
>>>> thanks
>>>>
>>>> Andrew
>>>>
>>>>
>>>> -----Original Message-----
>>>> From: Harbs [mailto:[email protected]]
>>>> Sent: 19 July 2018 12:18
>>>> To: [email protected]
>>>> Subject: [EXTERNAL] Re: Query on Royale dependency generation
>>>>
>>>> Are you using the -remove-circulars compiler option?
>>>>
>>>>> On Jul 19, 2018, at 1:05 PM, Frost, Andrew <[email protected]>
wrote:
>>>>>
>>>>> Hi guys
>>>>>
>>>>> I'd been getting an error when running a simple Royale application:
>>>>> Uncaught TypeError: Cannot read property 'BIG_ENDIAN' of undefined
>>>>> at new org.apache.royale.utils.BinaryData (BinaryData.js:28)
>>>>>
>>>>> the line in question is from the constructor:
>>>>> org.apache.royale.utils.BinaryData = function(bytes) { bytes =
>>>>> typeof bytes !== 'undefined' ? bytes : null; this._endian =
>>>>> org.apache.royale.utils.Endian.BIG_ENDIAN;
>>>>> and "Endian" is undefined.
>>>>>
>>>>> After a little digging I found this is because the BinaryData object
is being constructed without the JS engine having knowledge of the "Endian"
class: something went wrong with the google dependency thing. In my generated
html page I have a line:
>>>>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js'
>>>>> , ['org.apache.royale.utils.BinaryData'],
>>>>> ['org.apache.royale.utils.IBinaryDataInput',
>>>>> 'org.apache.royale.utils.IBinaryDataOutput']);
>>>>> and if I change this to:
>>>>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js'
>>>>> , ['org.apache.royale.utils.BinaryData'],
>>>>> ['org.apache.royale.utils.IBinaryDataInput',
>>>>> 'org.apache.royale.utils.IBinaryDataOutput',
>>>>> 'org.apache.royale.utils.Endian']);
>>>>> then it works.
>>>>>
>>>>> Looking at where this comes from in the compiler:
>>>>> compiler-jx/src/main/java/org/apache/royale/compiler/internal/graph
>>>>> / Go ogleDepsWriter.java function "generateDeps" is creating these
>>>>> lists, and if the "removeCirculars" value is true (which it is by
>>>>> default unless changed on the command-line) then we add dependencies
for the interfaces that we implement (gd.fileInfo.impls) and any static
dependencies (gd.fileInfo.staticDeps) but we don't add the actual dependencies
that were calculated (gd.deps or gd.fileInfo.deps - both of these contain the
Endian definition).
>>>>>
>>>>> So I can fix my project by updating the compiler to do:
>>>>> if (gd.fileInfo.deps != null)
>>>>> deps.addAll(gd.fileInfo.deps); and then it works: the
>>>>> generated line though is:
>>>>> goog.addDependency('../../../org/apache/royale/utils/BinaryData.js'
>>>>> , ['org.apache.royale.utils.BinaryData'], ['goog.DEBUG',
>>>>> 'org.apache.royale.utils.Endian',
>>>>> 'org.apache.royale.utils.IBinaryDataInput',
>>>>> 'org.apache.royale.utils.IBinaryDataOutput']);
>>>>>
>>>>>
>>>>> So my questions:
>>>>>
>>>>> 1. where is the fault here? Am I right in thinking that there's a
>>>>> missing set of dependencies that need to also be added per the above
snippet, or should the Endian definition be listed as a dependency in the
gd.fileInfo.staticDeps list (which is null for me) 2. presumably we don't
want "goog.DEBUG" to end up in the dependency list: is there a sensible way of
getting rid of this (or should we just manually filter out anything starting
"goog.")?
>>>>> 3. if we should be adding these dependencies separately, is there a
preference for "gd.deps" vs "gd.fileInfo.deps"?
>>>>>
>>>>> thanks
>>>>>
>>>>> Andrew
>>>>>
>>>>>
>>>>
>>>
>>
>