📜 ⬆️ ⬇️

Static variable initialized 2 times

Playing with singleton-static-constants in the flex, that's what ran into:


There are files ( github.com/radistao/test-super-and-static/commit/1f0a6 ):

ChatModel.as
package { public class ChatModel { public var someConst : String = getTrace("someConst"); public var someVar : String = getTrace("someVar"); public static const someStaticConst : String = getTrace("someStaticConst"); public static var someStaticVar : String = getTrace("someStaticVar"); public const menu : ChatMenuStructure = new ChatMenuStructure(); private static var _instance : ChatModel; public static function get instance() : ChatModel { trace("Get instance start"); if (!_instance) { _instance = new ChatModel(new T()); } trace("Get instance end"); return _instance; } public function ChatModel(t : T) { trace("ChatModel contructor 1"); } private static var getTraceCount : int = 0; public static function getTrace(str : String) : String { getTraceCount++; trace("getTrace #" + getTraceCount, str); return str; } } } internal class T{ } 

ChatMenuStructure.as
 package { public class ChatMenuStructure { public const chatMenuStructureConst : String = getTrace("chatMenuStructureConst"); public var chatMenuStructureVar : String = getTrace("chatMenuStructureVar"); public function ChatMenuStructure() { trace("ChatMenuStructure constructor"); } public static function getTrace(str : String) : String { trace("ChatMenuStructure getTrace", str); return str; } public function toString() : String { return "ChatMenuStructure instance"; } } } 

testSuperCall.mxml
 <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="creationCompleteHandler()"> <fx:Script> <![CDATA[ public var chatModel : ChatModel = ChatModel.instance; public const menu : ChatMenuStructure = chatModel.menu; protected function creationCompleteHandler():void { trace("menu is \"" + String(menu) + "\""); } ]]> </fx:Script> <fx:Declarations> <!-- Place non-visual elements (eg, services, value objects) here --> </fx:Declarations> </s:Application> 

')
Here are the traces that came out ( gist.github.com/radistao/5478426 ):
getTrace # 1 someStaticConst
getTrace # 2 someStaticVar
Get instance start
getTrace # 1 someConst
getTrace # 2 someVar
ChatMenuStructure getTrace chatMenuStructureConst
ChatMenuStructure getTrace chatMenuStructureVar
ChatMenuStructure constructor
ChatModel contructor 1
Get instance end
menu is "ChatMenuStructure instance"


Brief conclusions on the traces:


BUT!!!
while I did all this, I decided to enter the getTraceCount counter how many times it was called (line 32 of the ChatModel.as file) in the call to the static getTrace() method, which I used to initialize constants and variables. All I did to him was just increment and treysil with every getTrace() call

And now pay attention to the traces:
getTrace # 1 someStaticConst
getTrace # 2 someStaticVar
Get instance start
getTrace # 1 someConst
getTrace # 2 someVar


static variable getTraceCount initialized 2 times! WAT?

image

Hailing and collecting the remnants of my torn Mosca, I began to further cut and search, WTF! Well, in general, having missed all the busting options, dances with a tambourine and the calls of the spirits of the deceased flexers, I realized what the trampled

private static var getTraceCount : int = 0;


is in the code below the first two calls to getTrace () . Moving this ad to the very "top" of the class (code) - everything worked like a clock ( github.com/radistao/test-super-and-static/commit/a0dcd ):
ChatMenuStructure.as
 package { public class ChatModel { private static var getTraceCount : int = 0; public var someConst : String = getTrace("someConst"); public var someVar : String = getTrace("someVar"); public static const someStaticConst : String = getTrace("someStaticConst"); public static var someStaticVar : String = getTrace("someStaticVar"); public const menu : ChatMenuStructure = new ChatMenuStructure(); private static var _instance : ChatModel; public static function get instance() : ChatModel { trace("Get instance start"); if (!_instance) { _instance = new ChatModel(new T()); } trace("Get instance end"); return _instance; } public function ChatModel(t : T) { trace("ChatModel contructor before super()"); super(); trace("ChatModel contructor after super()"); } public static function getTrace(str : String) : String { getTraceCount++; trace("getTrace #" + getTraceCount, str); return str; } } } internal class T{ } 


Traces ( gist.github.com/radistao/5478443 ):
Treysy
 getTrace <b>#1</b> someStaticConst getTrace <b>#2</b> someStaticVar Get instance start getTrace <b>#3</b> someConst getTrace <b>#4</b> someVar ChatMenuStructure getTrace chatMenuStructureConst ChatMenuStructure getTrace chatMenuStructureVar ChatMenuStructure constructor ChatModel contructor before super() ChatModel contructor after super() Get instance end menu is "ChatMenuStructure instance" 


In general, something like this happens:
  1. getTrace is getTrace to initialize the first static object someStaticConst . In this getTrace() getTraceCount , initialized and incremented to 1
  2. getTrace is getTrace to initialize the second static object someStaticVar . In this getTrace() already initialized getTraceCount and incremented to 2.
  3. and here comes the initialization queue of the third static object — private static var getTraceCount declarations: int = 0; And in this place getTraceCount completely painlessly goes back to 0 and everything starts from the beginning!


image

Thus, it cannot be assumed that if a static variable is “at the bottom of the code” and will jerk earlier, then it will be correctly initialized.

Now I’m wondering who is to blame (and what to do): compiler, java-machine or flash player?

For the sake of interest, I experimented with non-primitive types ( github.com/radistao/test-super-and-static/commit/08894 ):

GetTracesClass.as
 package { public class GetTracesClass { private static var constructorCalls : int = 1; private var instanceNumber : int; public function GetTracesClass() { trace("GetTracesClass constructor called " + String(constructorCalls) + " time(s)"); instanceNumber = constructorCalls; constructorCalls++; } public function toString() : String { return "GetTracesClass instance #" + String(instanceNumber); } } } 


ChatModel.as
 package { public class ChatModel { public var someConst : String = getTrace("someConst"); public var someVar : String = getTrace("someVar"); public static const someStaticConst : String = getTrace("someStaticConst"); public static var someStaticVar : String = getTrace("someStaticVar"); public const menu : ChatMenuStructure = new ChatMenuStructure(); private static var _instance : ChatModel; public static function get instance() : ChatModel { trace("Get instance start"); if (!_instance) { _instance = new ChatModel(new T()); } trace("Get instance end"); return _instance; } public function ChatModel(t : T) { trace("ChatModel contructor before super()"); super(); trace("ChatModel contructor after super()"); } private static var getTraceCount : GetTracesClass = new GetTracesClass(); public static function getTrace(str : String) : String { trace("getTrace #\"" + getTraceCount + "\"", str); return str; } } } internal class T{ } 


Traces ( gist.github.com/radistao/5478479 ):
getTrace # "null" someStaticConst
getTrace # "null" someStaticVar
GetTracesClass constructor called 1 time (s)
Get instance start
getTrace # "GetTracesClass instance # 1" someConst
getTrace # "GetTracesClass instance # 1" someVar
ChatMenuStructure getTrace chatMenuStructureConst
ChatMenuStructure getTrace chatMenuStructureVar
ChatMenuStructure constructor
ChatModel contructor before super ()
ChatModel contructor after super ()
Get instance end
menu is "ChatMenuStructure instance"


There, everything is becoming more transparent, although it’s still stupid: the first 2 calls to getTrace() do not complain getTraceCount getTraceCount at getTraceCount , although they address it, and after its initialization everything goes further and further normally. If you initialize getTraceCount at the beginning, everything goes fine ( gist.github.com/radistao/5478482 )

image
(I didn’t have information to break the mosque, so I inserted this picture just like that!)

Then I tried it with Number - the same picture as with int .
Then I also tried to initialize getTraceCount not with zero, but 1 (i.e., not with default value): then " 2, 3, 2, 3 " fell in the traces (i.e., for the first time, all the same, it was taken not the default value for int , but the one that is confused by it in the initializer).

What can it affect? I don’t know for sure, but I suspect that it can probably affect some static instance counters of a particular class and similar patterns.

Thus, this article does not explain, but rather the opposite, asks where the problem is: in the compiler, in a Java machine or flash player? Is it possible to solve it? Personally, it seems to me that this behavior is not normal for a programming language.

Source: https://habr.com/ru/post/178367/


All Articles