Hello Jean-Pierre,
I had a second look in the problem and I found that the situation has
nothing to do with RTJ. I believe there is a bug with
SiteInliner/SynchronizeeManager classes that will potentially cause problems
to all applications with synchronized method. Given the fact some
implementation are JDK library methods are synchronised, well, you can say
all J2SE/J2ME apps are potentially compromised by this
bug.
I
have tried to reproduce the problem using JDK 1.3.1_01 (soot.Main --app
-soot-classpath c:\java\jdk1.3.1_01\jre\lib\rt.jar;.\ -O -W MEE):, and I
managed to reproduce the bug. The resultant jimple code looks almost identical
to your output.
I
have added some debug printouts in Soot and it shows two instance of method
inlining:
C:\sandbox\soottest\classes>java soot.Main --app --soot-classpath
c:\java\jdk1.3
.1_01\jre\lib\rt.jar;.\ -O -W -J MEE
Soot started on
Tue Apr 30 15:39:47 BST 2002
Adding inline
candiadates in StaticInliner
target: <MEE:
java.lang.Object newInstance(java.lang.Class)>
container: <MEE:
void essai()>
Statement: $r4 = staticinvoke <MEE: java.lang.Object
newInstance(java.lang.Class
)>($r3)
Adding inline
candiadate in StaticInliner
target: <MEE: void
essai()>
container: <MEE: void
main(java.lang.String[])>
Statement: staticinvoke <MEE: void
essai()>()
Transforming
MEE...
Soot finished on Tue Apr 30 15:39:51 BST 2002
Soot has run for
0 min. 4 sec.
In
both cases StaticInliner is inlining a static invokation with a static method
in the same class file (MEE). (Aside: IMO replacing static calls with inline
code does not produce much gain in speed and likely to increase size, and
therefore should not be incldued as a standard optimisation
techniques)
The
bug surfaces when SiteInliner attempts replace the static invokation of
<MEE: newInstance() inside MEE: essai()>.
The
method <MEE: java.lang.Object newInstance(java.lang.Class)> is a
synchronised method. It can potentially throw an exception. When an exception
is thrown inside newInstance() the control is passed back to the caller,
but at the same time the VM would automatically unlock the monitor (since it
is a synchronised method).
This
behaviour is not reproduced in the inlined code. The
SynchronizerManager does not correctly insert monitoexit code prior to the
athrow instructions. See the following commented Jimple fragments
for details:
<For your reference the original
newInstance()>
public static synchronized java.lang.Object
newInstance(java.lang.Class ) throws java.lang.InstantiationException,
java.lang.IllegalAccessException
{
java.lang.Class
r0;
java.lang.Object
r2;
java.lang.Throwable
$r3;
r0 := @parameter0:
java.lang.Class;
label0:
r2 = virtualinvoke
r0.<java.lang.Class: java.lang.Object
newInstance()>();
goto
label2;
label1:
$r3 :=
@caughtexception;
throw
$r3;
label2:
return
r2;
catch java.lang.Throwable
from label0 to label1 with label1;
}
<the method where inlining takes place>
public static void essai()
{
java.io.PrintStream $r0, $r5,
$r7;
java.lang.String
r1;
java.lang.Class $r3,
$r8;
java.lang.Exception
$r6;
java.lang.Throwable r9,
$r13;
java.lang.Object
r12;
$r0 = <java.lang.System:
java.io.PrintStream out>;
virtualinvoke $r0.<java.io.PrintStream: void
println(java.lang.String)>("Hello");
// The non-optimised version look like
this
// label0:
//
$r3 = staticinvoke <java.lang.Class: java.lang.Class
forName(java.lang.String)>("java.lang.Integer");
//
$r4 = staticinvoke <MEE: java.lang.Object
newInstance(java.lang.Class)>($r3);
//
r1 = (java.lang.String) $r4;
//
$r5 = <java.lang.System: java.io.PrintStream out>;
//
virtualinvoke $r5.<java.io.PrintStream: void
println(java.lang.String)>(r1);
label0:
$r3 = staticinvoke
<java.lang.Class: java.lang.Class
forName(java.lang.String)>("java.lang.Integer");
// Soot replaced staticinvoke <MEE: java.lang.Object
newInstance(java.lang.Class)>($r3) with this bunch of
code
$r8 = <MEE:
java.lang.Class class$MEE>;
if $r8 != null goto label1;
$r8 = staticinvoke <MEE:
java.lang.Class
class$(java.lang.String)>("MEE");
<MEE: java.lang.Class class$MEE> = $r8;
//
<Begin copy from newInstance()>
label1:
// SynchronizerManager
put this in to replace the automatic VM locking
entermonitor
$r8;
label2:
r12 = virtualinvoke
$r3.<java.lang.Class: java.lang.Object
newInstance()>();
goto
label4;
label3:
$r13 :=
@caughtexception;
// Synchronizer should put in an exitmonitor
function here but didn't
throw
$r13;
label4:
// SynchronizerManager
put this in to replace the automatic VM
unlocking
exitmonitor
$r8;
goto
label6;
//<Finish copy from
newInstance()>
label5:
r9 :=
@caughtexception;
exitmonitor
$r8;
throw
r9;
label6:
r1 = (java.lang.String)
r12;
$r5 = <java.lang.System:
java.io.PrintStream out>;
virtualinvoke $r5.<java.io.PrintStream: void
println(java.lang.String)>(r1);
label7:
goto
label9;
label8:
$r6 :=
@caughtexception;
label9:
$r7 =
<java.lang.System: java.io.PrintStream
out>;
virtualinvoke
$r7.<java.io.PrintStream: void
println(java.lang.String)>("Bye");
return;
catch java.lang.Throwable
from label2 to label3 with
label3;
catch
java.lang.Throwable from label2 to label4 with
label5;
catch
java.lang.Exception from label0 to label7 with label8;
}
I
don't belive this bug is easy to fix. We cannot simply add a monitorexit in
front of every athrow when inlining a synchronized method. Why? because some
of the athrows can be caught again in the same method if there is a suitable
exception block for it. Some heavy duty control flow analysis would be
required to fix this problem properly.
Regards,
Stephen