33import java .io .ByteArrayInputStream ;
44import java .lang .instrument .ClassFileTransformer ;
55import java .lang .instrument .IllegalClassFormatException ;
6- import java .lang .reflect .Modifier ;
76import java .security .ProtectionDomain ;
87
9- import javassist .CannotCompileException ;
8+ import gdxdbg .DisposableTransformer .ModifiedCloseable ;
9+ import gdxdbg .DisposableTransformer .ModifiedDisposible ;
1010import javassist .ClassPool ;
1111import javassist .CtClass ;
12- import javassist .CtField ;
13- import javassist .CtMethod ;
14- import javassist .CtNewMethod ;
1512import javassist .LoaderClassPath ;
16- import javassist .NotFoundException ;
1713
1814/**
1915 * {@link ClassFileTransformer} which performs class transformations to add various debugging utilities.
2016 * @author Desu
2117 */
2218public class DgbAgentClassFileTransformer implements ClassFileTransformer
2319{
24- public static final String FIELD_NAME_EXCEPTION = "$$$disposeexception" ;
25- public static final String FIELD_NAME_EXCEPTION_DOUBLE_DISPOSE = "$$$doubledisposeexception" ;
26-
20+ private DisposableTransformer disposableTransformer = new DisposableTransformer ("dispose" , "$$$disposeexception" , "com.badlogic.gdx.utils.Disposable" , ModifiedDisposible .class );
21+ private DisposableTransformer closeableTransformer = new DisposableTransformer ("close" , "$$$closeeexception" , "java.io.Closeable" , ModifiedCloseable .class );
2722 private ModifiableConstants modifiableConstants = new ModifiableConstants ();
2823 private GLThreadVerification glThreadVerification = new GLThreadVerification ();
29-
30- private static CtClass [] EMPTY_CT_CLASSES = new CtClass [0 ];
3124
3225 @ Override
3326 public byte [] transform (ClassLoader loader , String className , Class <?> classBeingRedefined , ProtectionDomain protectionDomain , byte [] classfileBuffer ) throws IllegalClassFormatException
3427 {
35- if (className .startsWith ("java/" ) || className .startsWith ("javax/" ))
28+ if (! Properties . DEBUG_UNCLOSED && ( className .startsWith ("java/" ) || className .startsWith ("javax/" ) ))
3629 return null ;
3730
3831 try
@@ -62,49 +55,13 @@ protected byte[] transformClass(ClassLoader loader, byte[] clazzBytes) throws Ex
6255 }
6356
6457 if (glThreadVerification .transform (clazz ))
65- {
6658 modified = true ;
67- }
6859
69- boolean isDisposable = false ;
70- try
71- {
72- CtClass disposable = cp .get ("com.badlogic.gdx.utils.Disposable" );
73- isDisposable = isDisposable (clazz , disposable );
74- }
75- catch (NotFoundException e )
76- {
77- // Don't care
78- }
79-
80- boolean foundValidDisposeMethod = false ;
81- if (isDisposable )
82- {
83- try
84- {
85- CtMethod method = clazz .getDeclaredMethod ("dispose" , EMPTY_CT_CLASSES );
86- foundValidDisposeMethod = isDisposible (method );
87- }
88- catch (NotFoundException e )
89- {
90- // Don't care
91- }
92- }
60+ if (Properties .DEBUG_UNDISPOSED && disposableTransformer .transform (cp , clazz ))
61+ modified = true ;
9362
94- if (foundValidDisposeMethod )
95- {
96- CtClass eo = cp .get (ModifiedDisposible .class .getName ());
97- for (CtClass i : clazz .getInterfaces ())
98- {
99- if (i .equals (eo ))
100- {
101- throw new RuntimeException ("Class already implements ModifiedDisposible interface" );
102- }
103- }
104-
105- writeDisposibleObjectImpl (clazz );
63+ if (Properties .DEBUG_UNCLOSED && closeableTransformer .transform (cp , clazz ))
10664 modified = true ;
107- }
10865
10966 if (modified )
11067 {
@@ -115,136 +72,4 @@ protected byte[] transformClass(ClassLoader loader, byte[] clazzBytes) throws Ex
11572
11673 return null ;
11774 }
118-
119- private boolean isDisposable (CtClass clazz , CtClass disposable ) throws Exception
120- {
121- if (clazz == null )
122- return false ;
123-
124- if (clazz .getName ().startsWith ("com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier" ) || clazz .getName ().startsWith ("com.badlogic.gdx.graphics.g3d.particles.ParticleControllerComponent" ))
125- return false ;
126-
127- // These aren't necessarily required to be disposed, and their sub resources will warn us of issues
128- if (clazz .getName ().startsWith ("com.badlogic.gdx.graphics.g2d.PixmapPacker" ) || clazz .getName ().startsWith ("com.badlogic.gdx.maps.Map" ))
129- return false ;
130-
131- if (clazz .equals (disposable ))
132- return true ;
133-
134- for (CtClass i : clazz .getInterfaces ())
135- {
136- if (isDisposable (i , disposable ))
137- return true ;
138- }
139-
140- if (isDisposable (clazz .getSuperclass (), disposable ))
141- return true ;
142-
143- return false ;
144- }
145-
146- protected void writeDisposibleObjectImpl (CtClass clazz ) throws NotFoundException , CannotCompileException
147- {
148- ClassPool cp = clazz .getClassPool ();
149- clazz .addInterface (cp .get (ModifiedDisposible .class .getName ()));
150- writeDisposibleObjectFields (clazz );
151- writeDisposibleObjectMethods (clazz );
152- }
153-
154- private void writeDisposibleObjectFields (CtClass clazz ) throws CannotCompileException , NotFoundException
155- {
156- ClassPool cp = clazz .getClassPool ();
157-
158- if (Properties .DEBUG_UNDISPOSED )
159- {
160- // add object that holds path from initialization
161- CtField cbField = new CtField (cp .get (RuntimeException .class .getName ()), FIELD_NAME_EXCEPTION , clazz );
162- cbField .setModifiers (Modifier .PRIVATE | Modifier .TRANSIENT );
163- clazz .addField (cbField , CtField .Initializer .byExpr ("new RuntimeException(\" Undisposed \" +getClass().getName()+\" resource\" );" ));
164- }
165-
166- if (Properties .DEBUG_DOUBLE_DISPOSE )
167- {
168- // add object that holds path from dispose for double dispose calls
169- CtField cbField = new CtField (cp .get (RuntimeException .class .getName ()), FIELD_NAME_EXCEPTION_DOUBLE_DISPOSE , clazz );
170- cbField .setModifiers (Modifier .PRIVATE | Modifier .TRANSIENT );
171- clazz .addField (cbField );
172- }
173- }
174-
175- private void writeDisposibleObjectMethods (CtClass clazz ) throws NotFoundException , CannotCompileException
176- {
177- if (Properties .DEBUG_UNDISPOSED )
178- {
179- CtMethod meth = null ;
180- try
181- {
182- meth = clazz .getDeclaredMethod ("finalize" , EMPTY_CT_CLASSES );
183- }
184- catch (NotFoundException e )
185- {
186- //Ignored
187- }
188-
189- StringBuilder sb = new StringBuilder ();
190- if (clazz .getName ().equals ("com.badlogic.gdx.graphics.GLTexture" ))
191- {
192- sb .append ("if((this instanceof com.badlogic.gdx.graphics.Texture) || (this instanceof com.badlogic.gdx.graphics.Cubemap)){} else " );
193- }
194- sb .append ("if(" +FIELD_NAME_EXCEPTION +" != null) " +FIELD_NAME_EXCEPTION +".printStackTrace();" );
195-
196- if (meth == null )
197- {
198- if (Properties .TRACE )
199- System .out .println ("finalize not found creating it" );
200- CtMethod m = CtNewMethod .make ("public void finalize() throws Throwable { " +sb .toString ()+"; super.finalize(); } " , clazz );
201- clazz .addMethod (m );
202- }
203- else
204- {
205- if (Properties .TRACE )
206- System .out .println ("modifing " + clazz .getName () + "'s finalize method" );
207- meth .insertBefore ("{ " +sb .toString ()+" }" );
208- }
209- }
210-
211- if (Properties .DEBUG_UNDISPOSED || Properties .DEBUG_DOUBLE_DISPOSE )
212- {
213- CtMethod meth = null ;
214- try
215- {
216- meth = clazz .getDeclaredMethod ("dispose" , EMPTY_CT_CLASSES );
217- }
218- catch (NotFoundException e )
219- {
220- //Ignored
221- }
222-
223- if (meth != null )
224- {
225- if (Properties .DEBUG_UNDISPOSED )
226- meth .insertBefore ("{ " +FIELD_NAME_EXCEPTION +" = null; }" );
227-
228- // Some are safe to double dispose
229- if (Properties .DEBUG_DOUBLE_DISPOSE && !"com.badlogic.gdx.graphics.Texture" .equals (clazz .getName ()))
230- meth .insertBefore ("{ synchronized(this) { if(" +FIELD_NAME_EXCEPTION_DOUBLE_DISPOSE +" != null) new RuntimeException(\" Double Dispose\" , " +FIELD_NAME_EXCEPTION_DOUBLE_DISPOSE +").printStackTrace(); "
231- + FIELD_NAME_EXCEPTION_DOUBLE_DISPOSE +" = new RuntimeException(\" Previous Dispose\" );"
232- + "} }" );
233- }
234- }
235- }
236-
237- protected boolean isDisposible (CtMethod method )
238- {
239- int modifiers = method .getModifiers ();
240- if (Modifier .isAbstract (modifiers ) || Modifier .isNative (modifiers ) || Modifier .isStatic (modifiers ))
241- return false ;
242-
243- return method .getName ().equals ("dispose" );
244- }
245-
246- public static interface ModifiedDisposible
247- {
248-
249- }
25075}
0 commit comments