A Little Sneak Peak

2 views
Skip to first unread message

maxim.porges

unread,
Mar 26, 2009, 12:16:49 AM3/26/09
to loom-as3
One of the tools I had to develop to figure out how to make Loom work
was a better version of the abcdump utility that comes with Tamarin.
The abcdump in Tamarin gives you much less information than Loom's
version, and I needed a lower level of detail to figure out what was
going on in the bytecode.

For simplicity, I implemented this new dump utility the toString()
method of the AbcFile domain object, so you can load the bytecode in
to the AbcFile and just trace it to get the dump of the file
structure, like in the code below. The ABC file embedded in the code
snippet was created by compiling a raw .as file with asc from the
command line. When Loom is finished it will include a utility that can
pull ABC blocks directly from a SWF.

[Embed(source="../../../main/flex/loom/template/DynamicSubClass.abc",
mimeType="application/octet-stream")]
private static var proxyTemplate : Class;
public static function getProxyTemplate() : ByteArray
{
return new proxyTemplate() as ByteArray;
}

public function testDumpProxyTemplate() : void
{
var proxyByteCode : ByteArray = getProxyTemplate();
var abcFile : AbcFile = new AbcDeserializer
(proxyByteCode).deserialize();
trace(abcFile);
}

Now for a quick description of the Loom objects. AbcFile is a domain
object filled with other domain objects representing the ConstantPool,
MethodInfo array, ClassInfo, InstanceInfo, MetaData, ScriptInfo, and
MethodBodies. This is a far cry from the code in the abcdump utility
in Tamarin, which is a 1700-line procedure that only prints strings
and doesn't produce any introspectable objects of any kind.

AbcFile can be reserialized back to bytecode with an AbcSerializer,
which is the symmetric opposite of AbcDeserializer. You can manipulate
the bytecode structure of a class defintion directly by adding to/
removing from/changing objects in an AbcFile, after which the
serializer will write them to a ByteArray. You can then load the
ByteArray in to the AVM with AbcClassLoader and get an instance-able
class definition, or write it somewhere else as you see fit. All this
stuff is done.

AbcFiles will be convertable to ClassDefinitions, which are the top-
level element of the introspection API. The nice thing about
ClassDefinition over the output from describeType() is that you can
introspect private functions and variables as well as public ones. The
ClassConverter utility object that performs this conversion is a work
in progress as I mentioned in my email earlier this week, but will
show up in an early point release of Loom.

Tracing the AbcFile instance from the code sample above produces the
traced class structure listed below. The opcodes are not printed in
the method bodies since I'm polishing off the opcode parser at the
moment. Once the opcodes are in there, this will be an invaluable tool
for determining if woven bytecode is properly structured; indeed, I've
already used it a lot so far to make sure Loom has been functioning
properly.

=========================================
Output from AbcFile's toString() method
=========================================

Integer Pool: 0
Uint Pool: 0
Double Pool: 0
String Pool:
*
loom.template:DynamicSubClass

Dictionary
DynamicSubClass.as$1
loom.template
loom.template:BaseClass
Object
flash.utils
__loomProxies
String
void
Function
methodName
Boolean
apply
MethodInvocation
methodCallOne
__loomProxyInvocation
int
Number
methodCallTwo
__loomSetClosure
DynamicSubClass
BaseClass
Namespace Pool:
Namespace[namespace::*]
Namespace[private::loom.template:DynamicSubClass]
Namespace[private::DynamicSubClass.as$1]
Namespace[public]
Namespace[public::loom.template]
Namespace[packageInternalNamespace::loom.template]
Namespace[protectedNamespace::loom.template:DynamicSubClass]
Namespace[staticProtectedNamespace::loom.template:DynamicSubClass]
Namespace[staticProtectedNamespace::loom.template:BaseClass]
Namespace[staticProtectedNamespace::Object]
Namespace[public::flash.utils]
Namespace[protectedNamespace::loom.template:BaseClass]
Namespace Set Pool:
[Namespace[namespace::*]]
[Namespace[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object], Namespace[public::flash.utils]]
[Namespace[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]
[Namespace[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object], Namespace
[protectedNamespace::loom.template:BaseClass]]
[Namespace[private::DynamicSubClass.as$1], Namespace[public],
Namespace[public::loom.template], Namespace
[packageInternalNamespace::loom.template]]
Multiname Pool:
QName[Namespace[namespace::*]:*]
Multiname[name=Dictionary, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object], Namespace[public::flash.utils]]]
Multiname[name=__loomProxies, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
QName[Namespace[public]:String]
Multiname_L[nsset=[Namespace[private::loom.template:DynamicSubClass],
Namespace[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
QName[Namespace[public]:void]
QName[Namespace[public]:Function]
Multiname[name=methodName, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
Multiname[name=Boolean, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
Multiname[name=apply, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
QName[Namespace[public::loom.template]:MethodInvocation]
Multiname[name=MethodInvocation, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
Multiname[name=methodCallOne, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object], Namespace
[protectedNamespace::loom.template:BaseClass]]]
Multiname[name=__loomProxyInvocation, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[protectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object]]]
QName[Namespace[public]:int]
QName[Namespace[public]:Number]
Multiname[name=methodCallTwo, nsset=[Namespace
[private::loom.template:DynamicSubClass], Namespace
[private::DynamicSubClass.as$1], Namespace[public], Namespace
[public::loom.template], Namespace
[packageInternalNamespace::loom.template], Namespace
[staticProtectedNamespace::loom.template:DynamicSubClass], Namespace
[staticProtectedNamespace::loom.template:BaseClass], Namespace
[staticProtectedNamespace::Object], Namespace
[protectedNamespace::loom.template:BaseClass]]]
QName[Namespace[public]:Object]
QName[Namespace[public]:methodCallTwo]
QName[Namespace[public]:methodCallOne]
QName[Namespace[public]:__loomSetClosure]
QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxies]
QName[Namespace[public::flash.utils]:Dictionary]
QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxyInvocation]
QName[Namespace[public::loom.template]:DynamicSubClass]
QName[Namespace[public::loom.template]:BaseClass]
Multiname[name=BaseClass, nsset=[Namespace[private::DynamicSubClass.as
$1], Namespace[public], Namespace[public::loom.template], Namespace
[packageInternalNamespace::loom.template]]]
Method Signatures (MethodInfo):
null function staticInitializer() : QName[Namespace[namespace::*]:*]
null function constructor(QName[Namespace[public]:String]) : QName
[Namespace[namespace::*]:*]
public function QName[Namespace[public]:__loomSetClosure](QName
[Namespace[public]:String], QName[Namespace[public]:Function]) : QName
[Namespace[public]:void]
private function QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxyInvocation](QName
[Namespace[public::loom.template]:MethodInvocation]) : QName[Namespace
[namespace::*]:*]
public function QName[Namespace[public]:methodCallOne](QName[Namespace
[public]:String], QName[Namespace[public]:Number]) : QName[Namespace
[public]:int]
public function QName[Namespace[public]:methodCallTwo](QName[Namespace
[public]:String], QName[Namespace[public]:Number], QName[Namespace
[public]:Object]) : QName[Namespace[public]:void]
null function scriptInitializer() : QName[Namespace[namespace::*]:*]

InstanceInfo[
className=QName[Namespace[public::loom.template]:DynamicSubClass]
superclassName=QName[Namespace[public::loom.template]:BaseClass]
isProtected=true
protectedNamespace=Namespace
[protectedNamespace::loom.template:DynamicSubClass]
interfaceCount=0
interfaces=
instanceInitializer=null function constructor(QName[Namespace
[public]:String]) : QName[Namespace[namespace::*]:*]
traits=[
MethodTrait[name=QName[Namespace[public]:methodCallTwo], metadata=,
dispositionId=0, method=public function QName[Namespace
[public]:methodCallTwo](QName[Namespace[public]:String], QName
[Namespace[public]:Number], QName[Namespace[public]:Object]) : QName
[Namespace[public]:void]]
MethodTrait[name=QName[Namespace[public]:methodCallOne], metadata=,
dispositionId=0, method=public function QName[Namespace
[public]:methodCallOne](QName[Namespace[public]:String], QName
[Namespace[public]:Number]) : QName[Namespace[public]:int]]
MethodTrait[name=QName[Namespace[public]:__loomSetClosure],
metadata=, dispositionId=0, method=public function QName[Namespace
[public]:__loomSetClosure](QName[Namespace[public]:String], QName
[Namespace[public]:Function]) : QName[Namespace[public]:void]]
SlotOrConstantTrait[name=QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxies], metadata=,
kind=slot, slot=0, typeName=QName[Namespace
[public::flash.utils]:Dictionary], vindex=0, vkind=null]
MethodTrait[name=QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxyInvocation],
metadata=, dispositionId=0, method=private function QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxyInvocation](QName
[Namespace[public::loom.template]:MethodInvocation]) : QName[Namespace
[namespace::*]:*]]
]
]
ClassInfo[
staticInitializer=null function staticInitializer() : QName[Namespace
[namespace::*]:*]
traits=[

]
]
ScriptInfo[
scriptInitializer=null function scriptInitializer() : QName[Namespace
[namespace::*]:*]
traits=[
ClassTrait[name=QName[Namespace
[public::loom.template]:DynamicSubClass], classSlotId=1, classIndex=0,
metadata=]
]
]

Methods Bodies:
null function staticInitializer() : QName[Namespace[namespace::*]:*]
{
//maxStack=1, localCount=1, initScopeDepth=4, maxScopeDepth=5
(3 opcodes)
}
traits=(no traits)

null function constructor(QName[Namespace[public]:String]) : QName
[Namespace[namespace::*]:*]
{
//maxStack=2, localCount=2, initScopeDepth=5, maxScopeDepth=6
(15 opcodes)
}
traits=(no traits)

public function QName[Namespace[public]:__loomSetClosure](QName
[Namespace[public]:String], QName[Namespace[public]:Function]) : QName
[Namespace[public]:void]
{
//maxStack=3, localCount=3, initScopeDepth=5, maxScopeDepth=6
(10 opcodes)
}
traits=(no traits)

private function QName[Namespace
[private::loom.template:DynamicSubClass]:__loomProxyInvocation](QName
[Namespace[public::loom.template]:MethodInvocation]) : QName[Namespace
[namespace::*]:*]
{
//maxStack=3, localCount=4, initScopeDepth=5, maxScopeDepth=6
(44 opcodes)
}
traits=(no traits)

public function QName[Namespace[public]:methodCallOne](QName[Namespace
[public]:String], QName[Namespace[public]:Number]) : QName[Namespace
[public]:int]
{
//maxStack=6, localCount=4, initScopeDepth=5, maxScopeDepth=6
(19 opcodes)
}
traits=(no traits)

public function QName[Namespace[public]:methodCallTwo](QName[Namespace
[public]:String], QName[Namespace[public]:Number], QName[Namespace
[public]:Object]) : QName[Namespace[public]:void]
{
//maxStack=6, localCount=5, initScopeDepth=5, maxScopeDepth=6
(21 opcodes)
}
traits=(no traits)

null function scriptInitializer() : QName[Namespace[namespace::*]:*]
{
//maxStack=2, localCount=1, initScopeDepth=1, maxScopeDepth=4
(25 opcodes)
}
traits=(no traits)

Josh McDonald

unread,
Mar 26, 2009, 12:28:56 AM3/26/09
to loom...@googlegroups.com
Awesome, we can use these Loom classes to build a *kickass* AIR app to poke around in (and mess with) our .swf files :)

-Josh

2009/3/26 maxim.porges <maxim....@gmail.com>



--
"Therefore, send not to know For whom the bell tolls. It tolls for thee."

Josh 'G-Funk' McDonald
  -  jo...@joshmcdonald.info
  -  http://twitter.com/sophistifunk
  -  http://flex.joshmcdonald.info/

maxim.porges

unread,
Mar 26, 2009, 12:52:07 AM3/26/09
to loom-as3
Absolutely. Loom's AbcFile is basically an opcode-level decompiler. If
somebody wanted to put the effort in to write an opcode-to-AScode
converter (or merge one in from an existing library) you'd be able to
view the source for an app pretty easily.

You can get to the opcodes easily through the MethodBody object. I
have an Opcode object in progress, which is basically an enum of all
159 opcodes in the AVM2 spec, plus an Op object which is an instance
of an Opcode along with the arguments to that instance.

package loom.abc
{
import mx.utils.StringUtil;

public class MethodBody
{
public var methodSignature : MethodInfo;
public var maxStack : int;
public var localCount : int;
public var initScopeDepth : int;
public var maxScopeDepth : int;
public var opcodes : Array;
public var exceptionInfos : Array;
public var traits : Array;

public function MethodBody()
{
opcodes = [];
exceptionInfos = [];
traits = [];
}
}
}

You'll be able to create an Op like so (or something similar, I'm
still messing with this part of the library). "constructsuper" is an
opcode that takes an integer as an argument. The integer tells the AVM
how many objects to pop off the stack; these objects become the
constructor args.

Opcode.constructsuper.op(3);

It's weird: there are loads of tools out there for messing with the
ABC file format and SWFs, but they all seemed very purpose-built. I
was able to take pieces from one or two to help make Loom, but I
either found that (a) I wasn't happy enough with the implementation of
each library to just use it directly, or (b) each library simply
lacked the generic level of detail required to do something other than
meet the original purpose of the library.

I'm hoping Loom will have a clean enough API to fill that void and
form the basis for other ABC-based projects. I guess we'll see!

- max


On Mar 26, 12:28 am, Josh McDonald <j...@joshmcdonald.info> wrote:
> Awesome, we can use these Loom classes to build a *kickass* AIR app to poke
> around in (and mess with) our .swf files :)
>
> -Josh
>
> 2009/3/26 maxim.porges <maxim.por...@gmail.com>
> ...
>
> read more »

Maxim Porges

unread,
Mar 26, 2009, 9:59:07 AM3/26/09
to loom...@googlegroups.com
Oh, one more note on this point: I do have long term plans for an Eclipse plugin that will invoke Loom and post-compile in the dynamic proxies in to SWFs. If anybody has experience with writing Eclipse plugins and is interested in pitching in, please let me know.

- max
Reply all
Reply to author
Forward
0 new messages