ABC - try, catch - Finally!

15 views
Skip to first unread message

Alen Balja

unread,
Feb 24, 2011, 5:01:50 PM2/24/11
to apparat-...@googlegroups.com
So, I made try-catch work with my code and the next step is to finally implement - finally. By reverse engineering and observing ABC it seems pretty complex (at the end of the email). It looks like one try/catch is first created, and then another one is applied around it. Something like this algorithm, am I remotely correct? 

1. Initially 

CallProp('trace)

2. Apply first try/catch

L0
CallProp('trace)
AbcQName('Error) => L2
L1
Jump(L3)
L2
// some code in catch block
L3
ReturnVoid()


3. Apply second try/catch (which in fact is finally)

L4
L0
CallProp('trace)
AbcQName('Error) => L2
L1
Jump(L3)
L2
// some code in catch block
L3
ReturnVoid()
AbcQName('null) => L6
L5
Jump(L7)
L6
//some code in finally block
L7
ReturnVoid()





Reverse Engineered Example 

privatefunction simpleTryCatchFinally():void

{

     try

     {

          trace("trying");

     }

     catch(e:Error)

     {

          trace("caught");

     }

     finally

     {

          trace("finally")

     }

}



It creates:


67 operations

2 exceptions, one of which is Error, one of which is 'null'

9 labels

No 'finally' block, but two 'try-catch', as they would be nested somehow 





Bytecode:

  operandStack: 7  

  scopeStack:   7  

  localCount:   4  

  2 exception(s):  

    (L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')), AbcQName('e,AbcNamespace(22,'))    

    (L3, L4) => L5, AbcQName('null,AbcNamespace(0,'null)), AbcQName('null,AbcNamespace(0,'null))    

  67 operation(s):  

            +0|-0  DebugFile('/Users/alenbalja/Projects/BBC/code2/ActionScriptTestCases/src;;EmptyFunction.as)    

            +0|-0  DebugLine(38)    

            +1|-0  GetLocal(0)    

            +0|-1  PushScope()    

            +1|-0  NewActivation()    

            +2|-1  Dup()    

            +0|-1  SetLocal(1)    

            +0|-1  PushScope()    

            +0|-0  Debug(1,'simpleTryCatchFinally$0,0,38)    

    try {    

      L3:     +0|-0  DebugLine(44)      

      try {      

        L0:     +1|-0  FindPropStrict(AbcQName('trace,AbcNamespace(22,')))        

                +1|-0  PushString('trying)        

                +1|-2  CallProperty(AbcQName('trace,AbcNamespace(22,')),1)        

                +0|-1  Pop()        

      } catch {      

        (AbcQName('e,AbcNamespace(22,')), AbcQName('Error,AbcNamespace(22,'))) => L2        

      }      

      L1:     +0|-0  DebugLine(47)      

              +0|-0  Jump(L6)      

      L2:     +1|-0  GetLocal(0)      

              +0|-1  PushScope()      

              +1|-0  GetLocal(1)      

              +0|-1  PushScope()      

              +1|-0  NewCatch((L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')), AbcQName('e,AbcNamespace(22,')))      

              +2|-1  Dup()      

              +0|-1  SetLocal(2)      

              +2|-1  Dup()      

              +0|-1  PushScope()      

              +2|-2  Swap()      

              +0|-2  SetSlot(1)      

              +0|-0  DebugLine(49)      

              +1|-0  FindPropStrict(AbcQName('trace,AbcNamespace(22,')))      

              +1|-0  PushString('caought)      

              +1|-2  CallProperty(AbcQName('trace,AbcNamespace(22,')),1)      

              +0|-1  Pop()      

              +0|-0  PopScope()      

              +0|-0  Kill(2)      

      L6:     +1|-0  PushByte(-1)      

    } catch {    

      (AbcQName('null,AbcNamespace(0,'null)), AbcQName('null,AbcNamespace(0,'null))) => L5      

    }    

    L4:     +0|-0  Jump(L7)    

    L5:     +1|-0  GetLocal(0)    

            +0|-1  PushScope()    

            +1|-0  GetLocal(1)    

            +0|-1  PushScope()    

            +1|-0  NewCatch((L3, L4) => L5, AbcQName('null,AbcNamespace(0,'null)), AbcQName('null,AbcNamespace(0,'null)))    

            +2|-1  Dup()    

            +0|-1  SetLocal(2)    

            +0|-1  PushScope()    

            +0|-0  PopScope()    

            +0|-0  Kill(2)    

            +1|-1  CoerceAny()    

            +0|-1  SetLocal(3)    

            +1|-0  PushByte(0)    

            +0|-0  Jump(L7)    

            +0|-0  Label()    

            +0|-1  Pop()    

    L9:     +0|-0  Label()    

            +1|-0  GetLocal(3)    

            +0|-0  Kill(3)    

            +0|-1  Throw()    

            +1|-0  PushByte(-1)    

            +0|-0  PopScope()    

            +0|-0  Kill(2)    

    L7:     +0|-0  DebugLine(55)    

            +1|-0  FindPropStrict(AbcQName('trace,AbcNamespace(22,')))    

            +1|-0  PushString('<finaly<)    

            +1|-2  CallProperty(AbcQName('trace,AbcNamespace(22,')),1)    

            +0|-1  Pop()    

            +0|-1  LookupSwitch(L8,[L9])    

    L8:     +0|-0  DebugLine(57)    

            +0|-0  ReturnVoid()    


pleclech

unread,
Feb 24, 2011, 5:25:48 PM2/24/11
to apparat-framework
I suggest you to read http://learn.adobe.com/wiki/display/AVM2/6.+Hints+for+compiler+writers
Finally is a little more complicated as it has to been always
executed

Patrick

On Feb 24, 11:01 pm, Alen Balja <alen.ba...@gmail.com> wrote:
> So, I made try-catch work with my code and the next step is to finally
> implement - finally. By reverse engineering and observing ABC it seems
> pretty complex (at the end of the email). It looks like one try/catch is
> first created, and then another one is applied around it. Something like
> this algorithm, am I remotely correct?
>
> *1. Initially *
>
> CallProp('trace)
>
> *2. Apply first try/catch*
>
> L0
> CallProp('trace)
> *AbcQName('Error) => L2*
> L1
> Jump(L3)
> L2
> // some code in catch block
> L3
> ReturnVoid()
>
> *3. Apply second try/catch (which in fact is finally)*
>
> *L4*
> L0
> CallProp('trace)
> *AbcQName('Error) => L2*
> L1
> Jump(L3)
> L2
> // some code in catch block
> L3
> ReturnVoid()
> *AbcQName('null) => L6*
> *L5*
> *Jump(L7)*
> *L6*
> *//some code in finally block*
> *L7*
> *ReturnVoid()*
> *
> *
> *
> *
> *
> *
> *
> *
> *
> *
> *Reverse Engineered Example *
> *
> *

Alen Balja

unread,
Feb 24, 2011, 5:26:50 PM2/24/11
to apparat-...@googlegroups.com, pleclech
Thanks for this, but is my idea generally valid? 

pleclech

unread,
Feb 24, 2011, 5:48:51 PM2/24/11
to apparat-framework
1. One part of the finally is to have it surrounded all your try/
catches block because you can have exception that have not been trap
by your try/catch and finally block must be executed even in this
case.
2. in your catch you can have a return , so you have to jump to the
code to be executed by the finally block and then do your return
3. you can have also a throw into your catch etc...

All these flows are represent by the compiler by a state machine
(everywhere you see a PushByte(xx)) and then dispatch using the
lookupswitch.

I suggest you to study how this is resolved by the compiler outputing
the dot graph from the apparat dump command and looking at the graph
produced using graphviz or another DOT graph viewer.

Best,
Patrick

Alen Balja

unread,
Feb 25, 2011, 4:09:41 PM2/25/11
to apparat-...@googlegroups.com
Which dump parameter produces dot graph? -swf, -abc, -uml,.... ?

Alen Balja

unread,
Feb 25, 2011, 4:49:51 PM2/25/11
to apparat-...@googlegroups.com
Ok, got it. Graphs are actually burried inside of what -abc produces
and you have to use -bc cfg option too. So do I have to copy paste the
graph out of that output? There seem to be several of them nested.


cat graph.dot | grep graph
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[
digraph G {
graph[

Joa Ebert

unread,
Feb 25, 2011, 5:34:16 PM2/25/11
to apparat-...@googlegroups.com
All methods are exported as single CFGs. That is the way dump works.
If you want only a single graph for a specific method you have to do
that manually using Scala (maybe 5 lines of code).


Best,

Joa

Alen Balja

unread,
Feb 25, 2011, 7:06:11 PM2/25/11
to apparat-...@googlegroups.com
The file as it gets produced by

dump -i myswf.swf -abc -bc cfg

is unreadable by GraphWiz. It's structure also doesn't seem to make sense in that form. However, there are structures inside that might resemble some dot graphs. I don't know how to easily extract them apart from manual copy paste, which could be extraordinary tedious for huge files.

Alen Balja

unread,
Feb 25, 2011, 7:49:49 PM2/25/11
to apparat-...@googlegroups.com
Even a single class with a constructor and single function seem to produce what seems to be 8 nested graphs.


cat EmptyFunction.swf.abc0.txt | grep graph
            digraph G {            
              graph[              
                  digraph G {                  
                    graph[                    
                  digraph G {                  
                    graph[                    
                      digraph G {                      
                        graph[        

pleclech

unread,
Feb 26, 2011, 5:21:09 AM2/26/11
to apparat-framework
Dot graph are not nested, but each method have it's own graph, just
look at the file within an editor. Each graph start with digraph G
{ and end with }

On Feb 26, 1:49 am, Alen Balja <alen.ba...@gmail.com> wrote:
> Even a single class with a constructor and single function seem to produce
> what seems to be 8 nested graphs.
>
> cat EmptyFunction.swf.abc0.txt | grep graph
>             digraph G {
>               graph[
>                   digraph G {
>                     graph[
>                   digraph G {
>                     graph[
>                       digraph G {
>                         graph[
>
>
>
>
>
>
>
> On Fri, Feb 25, 2011 at 7:06 PM, Alen Balja <alen.ba...@gmail.com> wrote:
> > The file as it gets produced by
>
> > *dump -i myswf.swf -abc -bc cfg*
>
> > is unreadable by GraphWiz. It's structure also doesn't seem to make sense
> > in that form. However, there are structures inside that might resemble some
> > dot graphs. I don't know how to easily extract them apart from manual copy
> > paste, which could be extraordinary tedious for huge files.
>
> > On Fri, Feb 25, 2011 at 5:34 PM, Joa Ebert <joaeb...@googlemail.com>
> > >> On Fri, Feb 25, 2011 at 4:09 PM, Alen Balja <alen.ba...@gmail.com>
> > wrote:
> > >>> Which dump parameter produces dot graph? -swf, -abc, -uml,.... ?
>
> > >>> On Thu, Feb 24, 2011 at 5:48 PM, pleclech <leclech.patr...@gmail.com>
> ...
>
> read more »

Joa Ebert

unread,
Feb 27, 2011, 7:42:50 AM2/27/11
to apparat-...@googlegroups.com
Alen: I wonder what your use-case is. If you want to get the graph for a single method quickly, use dump and copy it to a new file. It is dead simple. If you however need the graph for a specific method quite often you can use Scala do do that.`

import apparat.utils.IO._
import apparat.bytecode._
import java.io._

using(new PrintWriter(new FileOutputStream("graph.dot"))) {
writer => {
val bytecode = ... your code to find the bytecode ...
BytecodeControlFlowGraphBuilder(bytecode).dotExport to writer
}
}

Alen Balja

unread,
Feb 27, 2011, 1:03:57 PM2/27/11
to apparat-...@googlegroups.com
Yeah, it's pretty simple to find individual graphs. I was thinking in the beginning it is a single file single graph structure (being a DOT noob).
Reply all
Reply to author
Forward
0 new messages