How is Try / Catch handled in ABC

17 views
Skip to first unread message

Alen

unread,
Feb 18, 2011, 3:22:04 PM2/18/11
to apparat-framework
The syntax of ABC is fairly easily understandable with sequence of
operations. But I can't understand how to handle Try and Catch
statements if let's say I wanted to construct ABC List.

This is a simple trace

val someABC = FindPropStrict(traceName)::
PushString('foo)::
CallPropVoid(traceName, 1)::
Nil

But Try / Catch, when deconstructed looks like this. How can this be
represented in a Scala List?


Bytecode:
operandStack: 3
scopeStack: 5
localCount: 3
1 exception(s):
(L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')),
AbcQName('e,AbcNamespace(22,'))
69 operation(s):
+0|-0 DebugFile('/Users/alenbalja/Projects/BBC/code2/
ActionScriptTestCases/src;;SimpleException.as)
+0|-0 DebugLine(12)
+1|-0 GetLocal(0)
+0|-1 PushScope()
+1|-0 NewActivation()
+2|-1 Dup()
+0|-1 SetLocal(1)
+0|-1 PushScope()
+0|-0 Debug(1,'exceptionSurrounded$0,0,12)
+0|-0 DebugLine(14)
+1|-0 GetScopeObject(1)
+1|-0 PushFalse()
+1|-1 ConvertBoolean()
+0|-2 SetSlot(1)
+0|-0 DebugLine(15)
+1|-0
FindPropStrict(AbcQName('trace,AbcNamespace(22,')))
+1|-0 PushString('entry)
+1|-2 CallProperty(AbcQName('trace,AbcNamespace(22,')),
1)
+0|-1 Pop()
try {
L0: +0|-0 DebugLine(20)
+1|-0 PushByte(5)
+1|-0 PushByte(0)
+0|-2 IfNotGreaterThan(L3)
+0|-0 DebugLine(22)
+1|-0
FindPropStrict(AbcQName('Error,AbcNamespace(22,')))
+1|-0 PushString('zero bigger than five)
+1|-2 ConstructProp(AbcQName('Error,AbcNamespace(22,')),
1)
+0|-1 Throw()
+0|-0 Jump(L1)
L3: +0|-0 DebugLine(26)
+1|-0
FindPropStrict(AbcQName('trace,AbcNamespace(22,')))
+1|-0 PushString('exit)
+1|-2 CallProperty(AbcQName('trace,AbcNamespace(22,')),
1)
+0|-1 Pop()
+0|-0 DebugLine(27)
+1|-0 PushByte(4)
+0|-1 ReturnValue()
} catch {
(AbcQName('e,AbcNamespace(22,')),
AbcQName('Error,AbcNamespace(22,'))) => L2
}
L1: +0|-0 DebugLine(31)
+0|-0 Jump(L4)
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(33)
+1|-0
FindPropStrict(AbcQName('trace,AbcNamespace(22,')))
+1|-0 PushString('exit error)
+1|-2 CallProperty(AbcQName('trace,AbcNamespace(22,')),
1)
+0|-1 Pop()
+0|-0 DebugLine(34)
+1|-0 GetScopeObject(2)
+1|-1 GetSlot(1)
+0|-1 Throw()
+0|-0 PopScope()
+0|-0 Kill(2)
L4: +0|-0 DebugLine(37)
+1|-0
FindPropStrict(AbcQName('trace,AbcNamespace(22,')))
+1|-0 PushString('exit)
+1|-2 CallProperty(AbcQName('trace,AbcNamespace(22,')),
1)
+0|-1 Pop()
+0|-0 DebugLine(38)
+1|-0 PushByte(1)
+0|-1 ReturnValue()

Joa Ebert

unread,
Feb 19, 2011, 7:44:00 AM2/19/11
to apparat-...@googlegroups.com
Hi Alen,

the Bytecode class is used for this particular case since exceptions are part of additional metadata which is stored asides of the bytecode:


Also Apparat does not use any kind of Labels internally. Instead you must use the MarkerManager which will be responsible for inserting the correct Label operations when needed and encode the actual jumps to an offset in bytes.


Best,

Joa

Alen Balja

unread,
Feb 20, 2011, 9:31:03 PM2/20/11
to apparat-...@googlegroups.com
Is there an example of exceptions being applied to ByteArray?

Joa Ebert

unread,
Feb 21, 2011, 4:10:57 AM2/21/11
to apparat-...@googlegroups.com
I do not understand what you mean by that. What do you want to do?

Alen Balja

unread,
Feb 21, 2011, 10:09:45 AM2/21/11
to apparat-...@googlegroups.com
Sorry, I meant Bytecode not ByteArray :) 

When I'm constructing my own methods from ABC, I do it like this:

Bytecode.ops = List(some AbstractOp's here)

I guess if I want to add exceptions I have to do it like this:

Bytecode.exceptions = Array(some BytecodeExceptionHandler's)

I wonder if there is an example of this being applied anywhere?

Thanks

Joa Ebert

unread,
Feb 21, 2011, 1:51:07 PM2/21/11
to apparat-...@googlegroups.com
Not that I am aware of since I never created them.

I am sorry that you currently have to follow this route since ultimately a DSL for bytecode generation is what we want to have and I started with a little sketch.


Best,

Joa

Alen Balja

unread,
Feb 21, 2011, 2:00:18 PM2/21/11
to apparat-...@googlegroups.com
Is it that difficult? Could I ask for a tiny pseudo code example then?

Alen Balja

unread,
Feb 23, 2011, 12:26:40 PM2/23/11
to apparat-...@googlegroups.com
I got pretty close but still missing something. This is how I approached the problem, I want to insert an exception handler into a function with one simple trace. 

val firstPushScopeIndex = bytecode.ops.indexWhere( x => x ~== PushScope()) // insert try after first pushScope call

var op = bytecode.ops(firstPushScopeIndex + 1)

                   

val markerStart = bytecode.markers.mark(op) 

val markerCatch = bytecode.markers.mark(op) 

val markerEnd = bytecode.markers.mark(bytecode.ops(bytecode.ops.length - 2)) 

              

val typeName = AbcQName('Error, AbcNamespace(22, Symbol("")))

val varName = AbcQName('e, AbcNamespace(22, Symbol("")))

                   

val newExceptionHandler = new BytecodeExceptionHandler(markerStart, markerEnd, markerCatch, typeName, varName)

bytecode.exceptions = Array(newExceptionHandler) //:: bytecode.exceptions



This produces this ABC:

Bytecode:
  operandStack: 5  
  scopeStack:   1  
  localCount:   1  
  1 exception(s):  
    (L0, L1) => L0, AbcQName('Error,AbcNamespace(22,')), AbcQName('e,AbcNamespace(22,'))    
  11 operation(s):  
            +0|-0  DebugFile('/Users/alenbalja/Projects/BBC/code2/ActionScriptTestCases/src;;EmptyFunction.as)    
            +0|-0  DebugLine(18)    
            +1|-0  GetLocal(0)    
            +0|-1  PushScope()    
    try {    
      L0:     +0|-0  DebugLine(20)      
              +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,'))) => L0      
    }    
    L1:     +0|-0  DebugLine(21)    
            +0|-0  ReturnVoid()    



This _seems_ like it's something meaningful at the ABC level but when decompiling the resulting SWF, no try/catch statements are there. Insertion and SWF saving works for other things like traces in the same code.


Thanks


Alen

Joa Ebert

unread,
Feb 23, 2011, 1:11:27 PM2/23/11
to apparat-...@googlegroups.com
It looks like your catch handler is defined wrong or not defined at all.
I mean which decompiler are you using?

Right now, in pseudo code you have created this:

try {
  label:
  trace('trying...')
} catch(e: Error) {
  goto label
}

In fact if trace would throw an exception you get an infinite loop. However, how is it decompiled? Not every decompiler is able to handle such a case obviously. They will fail quite fast in face.


Best,

Joa

Alen Balja

unread,
Feb 23, 2011, 1:15:50 PM2/23/11
to apparat-...@googlegroups.com
I'm using Sothink SWF Decompiler, but the problem is most likely that my code is meaningless in AS world. Although, if you're correct, the code you wrote is almost what I'm after. All I need to do is add some ops into the catch block. 


However, after observing a similar function that I want to create, which is this:

privatefunction exceptionNoReturn():void

{

     try

     {

          trace("trying")

     }

     catch(e:Error)

     {

     }

}


The ABC looks different, i.e. there are Jump's, more markers, etc.... 



Bytecode:

  operandStack: 3 

  scopeStack:   3 

  localCount:   2 

  1 exception(s): 

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

  24 operation(s): 

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

            +0|-0  DebugLine(12)   

            +1|-0  GetLocal(0)   

            +0|-1  PushScope()   

    try {   

      L0:     +0|-0  DebugLine(16)     

              +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(18)   

            +0|-0  Jump(L3)   

    L2:     +1|-0  GetLocal(0)   

            +0|-1  PushScope()   

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

            +2|-1  Dup()   

            +0|-1  SetLocal(1)   

            +2|-1  Dup()   

            +0|-1  PushScope()   

            +2|-2  Swap()   

            +0|-2  SetSlot(1)   

            +0|-0  PopScope()   

            +0|-0  Kill(1)   

    L3:     +0|-0  DebugLine(21)   

            +0|-0  ReturnVoid()   

SimpleException/private:exceptionNoReturn

ops len: 24

markers: apparat.bytecode.MarkerManager@30d1e7c2

exceptions len: 1

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

pleclech

unread,
Feb 23, 2011, 2:00:01 PM2/23/11
to apparat-framework
Yes you need to insert a Jump after your trace to jump outside the
catch block otherwise you will always enter into that block even if
there is no exception.

Best,
Patrick.

On Feb 23, 7:15 pm, Alen Balja <alen.ba...@gmail.com> wrote:
> I'm using Sothink SWF Decompiler, but the problem is most likely that my
> code is meaningless in AS world. Although, if you're correct, the code you
> wrote is almost what I'm after. All I need to do is add some ops into the
> catch block.
>
> However, after observing a similar function that I want to create, which is
> this:
>
> privatefunction exceptionNoReturn():void
>
> {
>
>      try
>
>      {
>
>           trace("trying")
>
>      }
>
>      catch(e:Error)
>
>      {
>
>      }
>
> }
>
> The ABC looks different, i.e. there are Jump's, more markers, etc....
>
> *Bytecode:*
>
> *  operandStack: 3 *
>
> *  scopeStack:   3 *
>
> *  localCount:   2 *
>
> *  1 exception(s): *
>
> *    (L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')),
> AbcQName('e,AbcNamespace(22,'))   *
>
> *  24 operation(s): *
>
> *            +0|-0
> DebugFile('/Users/alenbalja/Projects/BBC/code2/ActionScriptTestCases/src;;* *
> SimpleException.as**)   *
>
> *            +0|-0  DebugLine(12)   *
>
> *            +1|-0  GetLocal(0)   *
>
> *            +0|-1  PushScope()   *
>
> *    try {   *
>
> *      L0:     +0|-0  DebugLine(16)     *
>
> *              +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(18)   *
>
> *            +0|-0  Jump(L3)   *
>
> *    L2:     +1|-0  GetLocal(0)   *
>
> *            +0|-1  PushScope()   *
>
> *            +1|-0  NewCatch((L0, L1) => L2,
> AbcQName('Error,AbcNamespace(22,')), AbcQName('e,AbcNamespace(22,')))   *
>
> *            +2|-1  Dup()   *
>
> *            +0|-1  SetLocal(1)   *
>
> *            +2|-1  Dup()   *
>
> *            +0|-1  PushScope()   *
>
> *            +2|-2  Swap()   *
>
> *            +0|-2  SetSlot(1)   *
>
> *            +0|-0  PopScope()   *
>
> *            +0|-0  Kill(1)   *
>
> *    L3:     +0|-0  DebugLine(21)   *
>
> *            +0|-0  ReturnVoid()   *
>
> *SimpleException/private:exceptionNoReturn*
>
> *ops len: 24*
>
> *markers: apparat.bytecode.MarkerManager@30d1e7c2*
>
> *exceptions len: 1*
>
> *(L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')),
> AbcQName('e,AbcNamespace(22,'))*
>
> **
> >  On Mon, Feb 21, 2011 at 2:00 PM, Alen Balja <alen.ba...@gmail.com> wrote:
>
> >> Is it that difficult? Could I ask for a tiny pseudo code example then?
>
> >> On Mon, Feb 21, 2011 at 1:51 PM, Joa Ebert <joaeb...@googlemail.com>wrote:
>
> >>>  Not that I am aware of since I never created them.
>
> >>> I am sorry that you currently have to follow this route since ultimately
> >>> a DSL for bytecode generation is what we want to have and I started with a
> >>> little sketch.
>
> >>> Best,
>
> >>> Joa
> >>> Am 21.02.2011 16:09, schrieb Alen Balja:
>
> >>> Sorry, I meant Bytecode not ByteArray :)
>
> >>>  When I'm constructing my own methods from ABC, I do it like this:
>
> >>>  Bytecode.ops = List(some AbstractOp's here)
>
> >>>  I guess if I want to add exceptions I have to do it like this:
>
> >>>  Bytecode.exceptions = Array(some BytecodeExceptionHandler's)
>
> >>>  I wonder if there is an example of this being applied anywhere?
>
> >>>  Thanks
>
> >>> On Mon, Feb 21, 2011 at 4:10 AM, Joa Ebert <joaeb...@googlemail.com>wrote:
>
> >>>> I do not understand what you mean by that. What do you want to do?
>
> >>>> Am 21.02.2011 03:31, schrieb Alen Balja:
> >>>> > Is there an example of exceptions being applied to ByteArray?
>
> >>>> > On Sat, Feb 19, 2011 at 7:44 AM, Joa Ebert <joaeb...@googlemail.com
> >>>>  > <mailto:joaeb...@googlemail.com>> wrote:
>
> >>>> >     Hi Alen,
>
> >>>> >     the Bytecode class is used for this particular case since
> >>>> exceptions
> >>>> >     are part of additional metadata which is stored asides of the
> >>>> bytecode:
>
> >>>>http://code.google.com/p/apparat/source/browse/apparat-core/src/main/...

Alen Balja

unread,
Feb 23, 2011, 2:26:47 PM2/23/11
to apparat-...@googlegroups.com
Thanks Patrick. So I need to mark the return and jump to there I guess. How about inserting a trace into the newly catch{} block? Do I have to use jump there as well? From where and to where?

pleclech

unread,
Feb 23, 2011, 5:01:23 PM2/23/11
to apparat-framework
Based on your example you can do something like that:

-----------------
val markers = bytecode.markers
val ops = bytecode.ops

val firstPushScopeIndex = ops.indexWhere(x => x ~== PushScope())

var op = ops(firstPushScopeIndex + 1)

val markerStart = markers.mark(op)
val beforeReturnOp = ops(ops.length - 2)

val returnOp=ops(ops.length - 1)
val exitTry = Jump(markers.mark(returnOp))

val markerEnd = markers.mark(exitTry)

val pubNS=AbcNamespace(22, Symbol(""))
var trace=AbcQName('trace, pubNS)

val catchBody=List(Pop(),GetLocal(0), PushScope(),
FindPropStrict(trace), PushString('catched), CallPropVoid(trace, 1))

val markerCatch = markers.mark(catchBody.head)

bytecode.replace(beforeReturnOp, List(beforeReturnOp, exitTry) :::
catchBody)

val typeName = AbcQName('Error, pubNS)

val varName = AbcQName('e, pubNS)

val newExceptionHandler = new BytecodeExceptionHandler(markerStart,
markerEnd, markerCatch, typeName, varName)
bytecode.exceptions = Array(newExceptionHandler)
---------------

You have to pay attention that at merge point you stack/scope/type
have to be the same.
When you enter the catch block the scope is cleared so you have to set
it back yourself as it was at the end of the try, this is why i have
added a GetLocal(0), PushScope(), also you get on the stack the error
that have been thrown hence the Pop().

Best,
Patrick

Joa Ebert

unread,
Feb 23, 2011, 5:29:28 PM2/23/11
to apparat-...@googlegroups.com
The catched exception is always on the stack in a catch scope. The stack
analysis should respect that but I am not sure at the moment actually.

Alen Balja

unread,
Feb 23, 2011, 7:05:07 PM2/23/11
to apparat-...@googlegroups.com
I tried Patrick's example and the inserted ABC produces a runnable SWF which produces expected traces, in other words - it works! However, the decompiler is still not happy.

Decompiler: 
private function simpleFunction() : void {

            trace("trying....");

            ;

            trace("cathed");

            return;

        }


Bytecode:
  operandStack: 5  
  scopeStack:   1  
  localCount:   1  
  1 exception(s):  
    (L0, L1) => L2, AbcQName('Error,AbcNamespace(22,')), AbcQName('e,AbcNamespace(22,'))    
  18 operation(s):  
            +0|-0  DebugFile('/Users/alenbalja/Projects/BBC/code2/ActionScriptTestCases/src;;EmptyFunction.as)    
            +0|-0  DebugLine(18)    
            +1|-0  GetLocal(0)    
            +0|-1  PushScope()    
    try {    
      L0:     +0|-0  DebugLine(20)      
              +1|-0  FindPropStrict(AbcQName('trace,AbcNamespace(22,')))      
              +1|-0  PushString('trying....)      
              +1|-2  CallProperty(AbcQName('trace,AbcNamespace(22,')),1)      
              +0|-1  Pop()      
              +0|-0  DebugLine(21)      
    } catch {    
      (AbcQName('e,AbcNamespace(22,')), AbcQName('Error,AbcNamespace(22,'))) => L2      
    }    
    L1:     +0|-0  Jump(L3)    
    L2:     +0|-1  Pop()    
            +1|-0  GetLocal(0)    
            +0|-1  PushScope()    
            +1|-0  FindPropStrict(AbcQName('trace,AbcNamespace(22,')))    
            +1|-0  PushString('catched )    
            +0|-2  CallPropVoid(AbcQName('trace,AbcNamespace(22,')),1)    
    L3:     +0|-0  ReturnVoid()    

pleclech

unread,
Feb 23, 2011, 7:58:21 PM2/23/11
to apparat-framework
Yes that's normal because, as Joa stated, it's not a full try/catch as
generated by a compiler, i think sothink is expecting the NewCatch op
and other 'magic' to finish the analyze,
in contrary as3sorcerer output a better code but as you see it miss
something ;)
try {
super();
trace("hello world");
this.t();
} catch(e:Error) {
!NULL!;
trace("catched");
};


Best,
Patrick.
Reply all
Reply to author
Forward
0 new messages