Analyzing JavaFX
- May 5th, 2010
- Posted in JavaFX
- Write comment
Today I wanted to analyze the internals of JavaFX. This post is the documentation – primarily made for myself. But maybe useful for somebody else, too…
Simple class
At first I tried to create a simple class with just one var of type String:
1 2 3 | public class Test{ var value:String = "asdf"; } |
And then I tried to take a look at the internals. Of course I did not decompile that stuff. Instead I used javap and Reflection to analyze it myself. The result could look like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import com.sun.javafx.runtime.FXBase; import com.sun.javafx.runtime.FXObject; import com.sun.javafx.runtime.annotation.Public; import com.sun.javafx.runtime.annotation.ScriptPrivate; import com.sun.javafx.runtime.annotation.SourceName; @Public public class Test extends FXBase implements FXObject { @ScriptPrivate @SourceName( "value" ) private String $value; public Test() { this( false ); initialize$( true ); } public Test( boolean paramBoolean ) { super( paramBoolean ); this.$value = "asdf"; } } |
So, what is interesting about this?
At first the name of the field is not so nice. Very hard to access using Reflection… But probably that is intended.
Every JavaFX class extends FXBase (or implement FXObject if extending another Java class).
Well, those information can be found within the sources of FXBase (look at src.zip)…
Changing var to def
What is the difference? There is just an annotation (@Def) added to the field…
Simple binding
Of course binding is the most interesting part for me. So let’s create add simple binding. Therefore we create a second var (”boundValue”) and bind it to value using bind value.
And now here comes the wow. Look at that monster javafxc has created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | import com.sun.javafx.runtime.Checks; import com.sun.javafx.runtime.FXBase; import com.sun.javafx.runtime.FXObject; import com.sun.javafx.runtime.annotation.Public; import com.sun.javafx.runtime.annotation.ScriptPrivate; import com.sun.javafx.runtime.annotation.SourceName; @Public public class Test extends FXBase implements FXObject { private static int VCNT$ = 2; public static final int VOFF$value = 0; public static final int VOFF$boundValue = 1; private short VFLG$value; private short VFLG$boundValue; @ScriptPrivate @SourceName( "value" ) private String $value; @ScriptPrivate @SourceName( "boundValue" ) private String $boundValue; public static int VCNT$() { return 2; } public int count$() { return 2; } private String get$value() { return this.$value; } private void invalidate$value( int paramInt ) { int i = this.VFLG$value & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; this.VFLG$value = ( short ) ( this.VFLG$value & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; notifyDependents$( 0, paramInt ); invalidate$boundValue( paramInt ); } private String get$boundValue() { if ( ( this.VFLG$boundValue & 0x18 ) == 0 ) { this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x400 ); } else if ( ( this.VFLG$boundValue & 0x104 ) == 260 ) { int i = this.VFLG$boundValue; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFE7 | 0x0 ); String str = get$value(); this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x200 ); if ( ( this.VFLG$boundValue & 0x5 ) == 4 ) { this.VFLG$boundValue = ( short ) i; return str; } this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFF8 | 0x19 ); this.$boundValue = str; } return this.$boundValue; } private void invalidate$boundValue( int paramInt ) { int i = this.VFLG$boundValue & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; if ( ( ( paramInt & 0x8 ) == 8 ) && ( ( this.VFLG$value & 0x5 ) == 4 ) ) return; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; } public void applyDefaults$( int paramInt ) { if ( !varTestBits$( paramInt, 56, 8 ) ) return; switch ( paramInt ) { case 0: String str = this.$value; int i = this.VFLG$value; this.VFLG$value = ( short ) ( this.VFLG$value | 0x18 ); if ( ( !Checks.equals( str, "asdf" ) ) || ( ( i & 0x10 ) == 0 ) ) { invalidate$value( 97 ); this.$value = "asdf"; invalidate$value( 94 ); } this.VFLG$value = ( short ) ( this.VFLG$value & 0xFFFFFFF8 | 0x1 ); return; case 1: invalidate$boundValue( 65 ); invalidate$boundValue( 92 ); if ( ( this.VFLG$boundValue & 0x440 ) != 0 ) get$boundValue(); return; } super.applyDefaults$( paramInt ); } public Object get$( int paramInt ) { switch ( paramInt ) { case 0: return get$value(); case 1: return get$boundValue(); } return super.get$( paramInt ); } public void set$( int paramInt, Object paramObject ) { switch ( paramInt ) { case 0: this.$value = ( ( String ) paramObject ); return; case 1: this.$boundValue = ( ( String ) paramObject ); return; } super.set$( paramInt, paramObject ); } public void invalidate$( int paramInt1, int paramInt2, int paramInt3, int paramInt4, int paramInt5 ) { switch ( paramInt1 ) { case 0: invalidate$value( paramInt5 ); return; case 1: invalidate$boundValue( paramInt5 ); return; } super.invalidate$( paramInt1, paramInt2, paramInt3, paramInt4, paramInt5 ); } public int varChangeBits$( int paramInt1, int paramInt2, int paramInt3 ) { switch ( paramInt1 ) { case 0: return this.VFLG$value = ( short ) ( this.VFLG$value & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); case 1: return this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); } return super.varChangeBits$( paramInt1, paramInt2, paramInt3 ); } public Test() { this( false ); initialize$( true ); } public Test( boolean paramBoolean ) { super( paramBoolean ); this.VFLG$value = 1; this.VFLG$boundValue = 769; this.$value = ""; this.$boundValue = ""; } } |
Very interesting. Let’s take a deeper look:
First two fields are created as expected.
Dirty flags
Additionally for both fields a short “VFLG$” has been generated. Those hold the dirty state of the var. There is also a static field (int) prefixed with “VOFF” for every var. Seems to hold some sort of index?
Getters
And now there have been created (private!) getters. The getter for the unbound var is simple. Just returns the field. The getter for boundValue is more interesting:
It contains some checks about its state using VFLG$boundValue. Depending on that state the field is updated (see line 61) using the getter for value. Here we can see that bindings are in fact lazy.
Invalidate methods
For every var there has been created an invalidate method. Those change the dirty flag and call notifyDependents$. That method delegates to DependentsManager.
Very interesting: The invalidate method for boudnValue does not call notifyDepenents. This is probably an optimization because the scope of the vars is script.
Generic methods
There are generic get/set/invalidate methods that delegate to the corresponding methods based on the index.
applyDefaults
This method is also interesting. It is called from FXBase#applyDefaults which itself is called within the constructor. I don’t know if it is called sometimes else. But probably it is…
Constructor
The constructor is different. It does not initialize the the fields. Instead they are set to their default values.
One var with on replace
To verify that thought we go one step back: Just one var, but this time we add a on replace method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | import com.sun.javafx.runtime.Checks; import com.sun.javafx.runtime.FXBase; import com.sun.javafx.runtime.FXObject; import com.sun.javafx.runtime.annotation.Public; import com.sun.javafx.runtime.annotation.ScriptPrivate; import com.sun.javafx.runtime.annotation.SourceName; import javafx.lang.Builtins; @Public public class Test extends FXBase implements FXObject { private static int VCNT$ = 1; public static final int VOFF$value = 0; private short VFLG$value; @ScriptPrivate @SourceName( "value" ) private String $value; public static int VCNT$() { return 1; } public int count$() { return 1; } private String get$value() { return this.$value; } private void onReplace$value( String paramString1, String paramString2 ) { Builtins.println( String.format( "value changed to %s", new Object[]{get$value()} ) ); } public void applyDefaults$( int paramInt ) { if ( !varTestBits$( paramInt, 56, 8 ) ) return; switch ( paramInt ) { case 0: String str = this.$value; int i = this.VFLG$value; this.VFLG$value = ( short ) ( this.VFLG$value | 0x18 ); if ( ( !Checks.equals( str, "asdf" ) ) || ( ( i & 0x10 ) == 0 ) ) { this.$value = "asdf"; onReplace$value( str, "asdf" ); } return; } super.applyDefaults$( paramInt ); } public Object get$( int paramInt ) { switch ( paramInt ) { case 0: return get$value(); } return super.get$( paramInt ); } public void set$( int paramInt, Object paramObject ) { switch ( paramInt ) { case 0: this.$value = ( ( String ) paramObject ); return; } super.set$( paramInt, paramObject ); } public int varChangeBits$( int paramInt1, int paramInt2, int paramInt3 ) { switch ( paramInt1 ) { case 0: return this.VFLG$value = ( short ) ( this.VFLG$value & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); } return super.varChangeBits$( paramInt1, paramInt2, paramInt3 ); } public Test() { this( false ); initialize$( true ); } public Test( boolean paramBoolean ) { super( paramBoolean ); this.VFLG$value = 65; this.$value = ""; } } |
What is the difference?
The same stuff is created as in the last example. Of course this is necessary because the on replace method is part of the binding stuff. The difference is, that there has been created a onReplace$value method with two parameters (don’t know what those parameters represent).
This method is only called in applyDefaults. Probably another optimization.
Two public-read vars with a on replace method
This results in something like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | package com.cedarsoft.collustra; import com.sun.javafx.runtime.Checks; import com.sun.javafx.runtime.FXBase; import com.sun.javafx.runtime.FXObject; import com.sun.javafx.runtime.annotation.Public; import com.sun.javafx.runtime.annotation.PublicReadable; import com.sun.javafx.runtime.annotation.ScriptPrivate; import com.sun.javafx.runtime.annotation.SourceName; import javafx.lang.Builtins; @Public public class Test extends FXBase implements FXObject { private static int VCNT$ = 2; public static int VOFF$value = 0; public static int VOFF$boundValue = 1; public short VFLG$value; public short VFLG$boundValue; @PublicReadable @ScriptPrivate @SourceName( "value" ) public String $value; @PublicReadable @ScriptPrivate @SourceName( "boundValue" ) public String $boundValue; public static int VCNT$() { return 2; } public int count$() { return 2; } public String get$value() { return this.$value; } public String set$value( String paramString ) { if ( ( this.VFLG$value & 0x200 ) != 0 ) restrictSet$( this.VFLG$value ); String str = this.$value; int i = this.VFLG$value; this.VFLG$value = ( short ) ( this.VFLG$value | 0x18 ); if ( ( !Checks.equals( str, paramString ) ) || ( ( i & 0x10 ) == 0 ) ) { invalidate$value( 97 ); this.$value = paramString; invalidate$value( 94 ); onReplace$value( str, paramString ); } this.VFLG$value = ( short ) ( this.VFLG$value & 0xFFFFFFF8 | 0x1 ); return this.$value; } public void invalidate$value( int paramInt ) { int i = this.VFLG$value & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; this.VFLG$value = ( short ) ( this.VFLG$value & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; notifyDependents$( VOFF$value, paramInt ); invalidate$boundValue( paramInt ); if ( ( ( paramInt & 0x8 ) != 8 ) || ( ( this.VFLG$value & 0x40 ) != 64 ) ) return; get$value(); } public void onReplace$value( String paramString1, String paramString2 ) { Builtins.println( String.format( "value changed to %s", new Object[]{get$value()} ) ); } public String get$boundValue() { if ( ( this.VFLG$boundValue & 0x18 ) == 0 ) { this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x400 ); } else if ( ( this.VFLG$boundValue & 0x104 ) == 260 ) { int i = this.VFLG$boundValue; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFE7 | 0x0 ); String str1 = get$value(); this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x200 ); if ( ( this.VFLG$boundValue & 0x5 ) == 4 ) { this.VFLG$boundValue = ( short ) i; return str1; } String str2 = this.$boundValue; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFF8 | 0x19 ); if ( ( !Checks.equals( str2, str1 ) ) || ( ( i & 0x10 ) == 0 ) ) { this.$boundValue = str1; onReplace$boundValue( str2, str1 ); } } return this.$boundValue; } public String set$boundValue( String paramString ) { if ( ( this.VFLG$boundValue & 0x200 ) != 0 ) restrictSet$( this.VFLG$boundValue ); this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x200 ); String str = this.$boundValue; int i = this.VFLG$boundValue; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue | 0x18 ); if ( ( !Checks.equals( str, paramString ) ) || ( ( i & 0x10 ) == 0 ) ) { invalidate$boundValue( 97 ); this.$boundValue = paramString; invalidate$boundValue( 94 ); onReplace$boundValue( str, paramString ); } this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFF8 | 0x1 ); return this.$boundValue; } public void invalidate$boundValue( int paramInt ) { int i = this.VFLG$boundValue & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; if ( ( ( paramInt & 0x8 ) == 8 ) && ( ( this.VFLG$value & 0x5 ) == 4 ) ) return; this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; notifyDependents$( VOFF$boundValue, paramInt ); } public void onReplace$boundValue( String paramString1, String paramString2 ) { } public void applyDefaults$( int paramInt ) { if ( !varTestBits$( paramInt, 56, 8 ) ) return; switch ( paramInt ) { case 0: set$value( "asdf" ); return; case 1: invalidate$boundValue( 65 ); invalidate$boundValue( 92 ); if ( ( this.VFLG$boundValue & 0x440 ) != 0 ) get$boundValue(); return; } super.applyDefaults$( paramInt ); } public Object get$( int paramInt ) { switch ( paramInt ) { case 0: return get$value(); case 1: return get$boundValue(); } return super.get$( paramInt ); } public void set$( int paramInt, Object paramObject ) { switch ( paramInt ) { case 0: set$value( ( String ) paramObject ); return; case 1: set$boundValue( ( String ) paramObject ); return; } super.set$( paramInt, paramObject ); } public void invalidate$( int paramInt1, int paramInt2, int paramInt3, int paramInt4, int paramInt5 ) { switch ( paramInt1 ) { case 0: invalidate$value( paramInt5 ); return; case 1: invalidate$boundValue( paramInt5 ); return; } super.invalidate$( paramInt1, paramInt2, paramInt3, paramInt4, paramInt5 ); } public int varChangeBits$( int paramInt1, int paramInt2, int paramInt3 ) { switch ( paramInt1 ) { case 0: return this.VFLG$value = ( short ) ( this.VFLG$value & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); case 1: return this.VFLG$boundValue = ( short ) ( this.VFLG$boundValue & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); } return super.varChangeBits$( paramInt1, paramInt2, paramInt3 ); } public Test() { this( false ); initialize$( true ); } public Test( boolean paramBoolean ) { super( paramBoolean ); this.VFLG$value = 65; this.VFLG$boundValue = 769; this.$value = ""; this.$boundValue = ""; } } |
And now (see line 51) the call to onReplace has been added to the setter for value. Now we also understand the meaning of the parameters. The first one is the old value while the second one is the new one.
Interestingly there also has been genereated an empty onReplace method for boundValue. Propably because of overriding stuff?
The getter and setter are both public now. But I just set the scope to public-read. So don’t understand exactly why it is necessary to make the setter public, too.
Binding from other class
Now we create a sample where we bind from another class. Maybe we can learn something new.
1 2 3 4 | public class Test2{ var test = Test{}; public var otherBound = bind test.value; } |
And here is the pseudo java code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | import com.sun.javafx.runtime.Checks; import com.sun.javafx.runtime.FXBase; import com.sun.javafx.runtime.FXObject; import com.sun.javafx.runtime.annotation.Public; import com.sun.javafx.runtime.annotation.ScriptPrivate; import com.sun.javafx.runtime.annotation.SourceName; @Public public class Test2 extends FXBase implements FXObject { private static int VCNT$ = 2; public static final int VOFF$test = 0; public static int VOFF$otherBound = 1; private short VFLG$test; public short VFLG$otherBound; @ScriptPrivate @SourceName( "test" ) private Test $test; @Public @SourceName( "otherBound" ) public String $otherBound; private static int DCNT$ = 1; public static final int DEP$test$_$value = 0; public static int VCNT$() { return 2; } public int count$() { return 2; } private Test get$test() { return this.$test; } private void invalidate$test( int paramInt ) { int i = this.VFLG$test & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; this.VFLG$test = ( short ) ( this.VFLG$test & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; notifyDependents$( 0, paramInt ); invalidate$otherBound( paramInt ); } private void onReplace$test( Test paramTest1, Test paramTest2 ) { int i = Test.VOFF$value; FXBase.switchDependence$( this, paramTest1, i, paramTest2, i, 0 ); } public String get$otherBound() { if ( ( this.VFLG$otherBound & 0x18 ) == 0 ) { this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound | 0x400 ); } else if ( ( this.VFLG$otherBound & 0x104 ) == 260 ) { int i = this.VFLG$otherBound; this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound & 0xFFFFFFE7 | 0x0 ); String str1 = ( get$test() != null ) ? get$test().get$value() : ""; this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound | 0x200 ); if ( ( this.VFLG$otherBound & 0x5 ) == 4 ) { this.VFLG$otherBound = ( short ) i; return str1; } String str2 = this.$otherBound; this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound & 0xFFFFFFF8 | 0x19 ); if ( ( !Checks.equals( str2, str1 ) ) || ( ( i & 0x10 ) == 0 ) ) { this.$otherBound = str1; onReplace$otherBound( str2, str1 ); } } return this.$otherBound; } public String set$otherBound( String paramString ) { if ( ( this.VFLG$otherBound & 0x200 ) != 0 ) restrictSet$( this.VFLG$otherBound ); this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound | 0x200 ); String str = this.$otherBound; int i = this.VFLG$otherBound; this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound | 0x18 ); if ( ( !Checks.equals( str, paramString ) ) || ( ( i & 0x10 ) == 0 ) ) { invalidate$otherBound( 97 ); this.$otherBound = paramString; invalidate$otherBound( 94 ); onReplace$otherBound( str, paramString ); } this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound & 0xFFFFFFF8 | 0x1 ); return this.$otherBound; } public void invalidate$otherBound( int paramInt ) { int i = this.VFLG$otherBound & 0x7; int j = ( ( i & paramInt ) == i ) ? 1 : 0; if ( j == 0 ) return; if ( ( ( paramInt & 0x8 ) == 8 ) && ( ( this.VFLG$test & 0x5 ) == 4 ) ) return; this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound & 0xFFFFFFF8 | paramInt >> 4 ); paramInt &= -35; notifyDependents$( VOFF$otherBound, paramInt ); } public void onReplace$otherBound( String paramString1, String paramString2 ) { } public void applyDefaults$( int paramInt ) { if ( !varTestBits$( paramInt, 56, 8 ) ) return; switch ( paramInt ) { case 0: Test localTest1 = new Test(); Test localTest2 = this.$test; int i = this.VFLG$test; this.VFLG$test = ( short ) ( this.VFLG$test | 0x18 ); if ( ( localTest2 != localTest1 ) || ( ( i & 0x10 ) == 0 ) ) { invalidate$test( 97 ); this.$test = localTest1; invalidate$test( 94 ); onReplace$test( localTest2, localTest1 ); } this.VFLG$test = ( short ) ( this.VFLG$test & 0xFFFFFFF8 | 0x1 ); return; case 1: invalidate$otherBound( 65 ); invalidate$otherBound( 92 ); if ( ( this.VFLG$otherBound & 0x440 ) != 0 ) get$otherBound(); return; } super.applyDefaults$( paramInt ); } public static int DCNT$() { return 1; } public boolean update$( FXObject paramFXObject, int paramInt1, int paramInt2, int paramInt3, int paramInt4, int paramInt5 ) { switch ( paramInt1 ) { case 0: if ( paramFXObject == this.$test ) { invalidate$otherBound( paramInt5 ); return true; } } return super.update$( paramFXObject, paramInt1, paramInt2, paramInt3, paramInt4, paramInt5 ); return false; } public Object get$( int paramInt ) { switch ( paramInt ) { case 0: return get$test(); case 1: return get$otherBound(); } return super.get$( paramInt ); } public void set$( int paramInt, Object paramObject ) { switch ( paramInt ) { case 0: this.$test = ( ( Test ) paramObject ); return; case 1: set$otherBound( ( String ) paramObject ); return; } super.set$( paramInt, paramObject ); } public void invalidate$( int paramInt1, int paramInt2, int paramInt3, int paramInt4, int paramInt5 ) { switch ( paramInt1 ) { case 0: invalidate$test( paramInt5 ); return; case 1: invalidate$otherBound( paramInt5 ); return; } super.invalidate$( paramInt1, paramInt2, paramInt3, paramInt4, paramInt5 ); } public int varChangeBits$( int paramInt1, int paramInt2, int paramInt3 ) { switch ( paramInt1 ) { case 0: return this.VFLG$test = ( short ) ( this.VFLG$test & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); case 1: return this.VFLG$otherBound = ( short ) ( this.VFLG$otherBound & ( paramInt2 ^ 0xFFFFFFFF ) | paramInt3 ); } return super.varChangeBits$( paramInt1, paramInt2, paramInt3 ); } public Test2() { this( false ); initialize$( true ); } public Test2( boolean paramBoolean ) { super( paramBoolean ); this.VFLG$test = 1; this.VFLG$otherBound = 769; this.$otherBound = ""; } } |
The most interesting part is at line 52: There a call to FXBase.switchDependence$ is made every time the test reference is changing. This method delegates to DependentsManager#addDependent.
This is where the magic happens. I think I will take a deeper look at that stuff later.
Initialzing objects with parameters
If objects are created using the object literal, the JavaFX compiler generates the calls to set the initial value and calls complete$() on the newly created object finally.
Within complete$() the userInit$() and postInit$() methods are called.
Conclusion regarding binding
I hoped to find a way how to bind JavaFX objects to Java objects providing PropertyChangeEvents. Investigating the DependentsManager further might give further hints.
At the moment it looks a little bit difficult since the DependentsManager is working on FXObjects. So it is very probable that at least some kind of adapter objects is necessary… But who knows….
Nide reasearch and post on JavaFX internals. I am sure this serves as invaluable reference to me and others poking into JavaFX internals and exploring AOP with JavaFX