Loom Update

0 views
Skip to first unread message

maxim.porges

unread,
Mar 24, 2009, 2:45:46 AM3/24/09
to loom-as3
Hello all,

Sorry for the dead air for the last few weeks - I have been busy
working on Loom.

Tonight, I have some good news for you guys: I just got finished
testing the round-trip ABC parsing and reserialization support. This
means Loom is able to read ABC bytecode in its raw form, decompose it
in to Loom domain objects, recompose it back in to ABC code, and load
it in to the AVM for verification/class instantiation. This is a
milestone step for the library, since it means I'm days away from
making a closure-based AOP proxy.

Next steps will be changing class/superclass names on an existing ABC
file, and replacing the opcodes in a method with something simple
(probably a "Hello from Loom" trace to start off with). Once this is
done, I'll weave in the methods and properties required to support
closures and we should be ready to go with asynchronous proxy creation
with AOP support. At that point I'll send out an alpha library to
everybody on the list to play with and provide API feedback. The alpha
should be far enough along that you can start integrating it in to
your libraries.

Last step before the public beta release will be to write the SWF
module loader that can pull out the ABC code blocks from a SWF. The
loader will have a mechanism so that users can indicate which classes
they want proxies created for at load time. After the loader inspects
the classes and generates proxies, it will add the proxy ABC blocks to
the end of the SWF. Then, when the AVM loads the SWF, it will have no
idea that the proxies were created by Loom and not the compiler.
Although the SWF-loading itself will be asynchronous as usual,
everything will appear to have been synchronous from your code's
perspective; this is because the code in the SWF won't start executing
until Loom has finished enhancing it with the proxies.

I have the stubs for the reflection API ready, but need to finish the
ABC-to-ClassDefinition converter. I had originally planned to create
the proxies with the reflection API, but I'm at the point where I can
short-circuit the library to create AOP proxies sooner if I skip
finishing reflection for now. I honestly don't think a lot of people
are going to use the reflection API to generate bytecode, so I
deferred this to being a lower priority feature. Expect to see
reflection support in an early point release.

Keep your eyes peeled for the alpha library within a week or so.

Cheers,

- max

Brian

unread,
Mar 24, 2009, 9:21:20 AM3/24/09
to loom-as3
@max - This is great news! Keep up the great work man.

Robert Penner

unread,
Mar 24, 2009, 2:58:35 PM3/24/09
to loom...@googlegroups.com
This sounds amazing--thanks for the update!

Robert

Josh McDonald

unread,
Mar 24, 2009, 6:27:47 PM3/24/09
to loom...@googlegroups.com
Awesome news Max, can't wait to try it out!

-Josh

2009/3/24 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/

Drew Bourne

unread,
Mar 24, 2009, 6:44:18 PM3/24/09
to loom...@googlegroups.com
Thanks for the update Max, my fingers are itching to get coding with Loom.

cheers,
Drew

maxim.porges

unread,
Apr 7, 2009, 12:31:27 AM4/7/09
to loom-as3
Wow, this stuff is actually starting to work... I just dynamically
wove in the Dictionary for holding Loom handler mappings in to a
compiled ABC class, got the AVM to accept the modified bytecode, and
was able to see the new property show up in describeType(). I was also
able to set the value of the property to a new Dictionary from
external code and use said Dictionary without issue.

See the entry for handlerMappings in the loom namespace embedded in
the describeType() output below (last entry). Note that prior to the
weaving taking place, this class had no idea what a Dictionary was.

<type name="loom.template::BaseClass" base="Class" isDynamic="true"
isFinal="true" isStatic="true">
<extendsClass type="Class"/>
<extendsClass type="Object"/>
<accessor name="prototype" access="readonly" type="*"
declaredBy="Class"/>
<factory type="loom.template::BaseClass">
<extendsClass type="Object"/>
<constructor>
<parameter index="1" type="*" optional="false"/>
<parameter index="2" type="*" optional="false"/>
</constructor>
<method name="methodCallThree"
declaredBy="loom.template::BaseClass" returnType="String">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="methodCallOne" declaredBy="loom.template::BaseClass"
returnType="int">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="methodCallTwo" declaredBy="loom.template::BaseClass"
returnType="void">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
<parameter index="3" type="Object" optional="false"/>
</method>
<variable name="handlerMappings" type="flash.utils::Dictionary"
uri="http://loom.ninjitsoft.com"/>
</factory>
</type>


Here's the code I used to weave in the new trait in to the instance
info for the class. This will basically be the same process for
weaving in the Loom methods, only using MethodTrait instead of
SlotOrConstantTrait. I'll be hitting up these method traits next along
with their opcodes, followed by the modifications to the constructor
and static initializer opcodes, and then we should finally be in AOP-
land!

...
var flashUtilsNamespace : LNamespace = new LNamespace
(NamespaceKind.PACKAGE_NAMESPACE, "flash.utils");
var loomNamespace : LNamespace = new LNamespace
(NamespaceKind.PACKAGE_NAMESPACE, loomNamespaceName);
var handlerMappingsMultiname : QualifiedName = new
QualifiedName("handlerMappings", loomNamespace);
var dictionaryMultiname : QualifiedName = new QualifiedName
("Dictionary", flashUtilsNamespace);
...
// Add the handlerMappings element to the InstanceInfo
traits
var instance : InstanceInfo = abcFile.instanceInfo
[classIndex];
var handlerMappingsTrait : SlotOrConstantTrait = new
SlotOrConstantTrait();
handlerMappingsTrait.traitMultiname =
handlerMappingsMultiname;
handlerMappingsTrait.traitKind = TraitKind.SLOT;
handlerMappingsTrait.slotId = 0;
handlerMappingsTrait.typeMultiname = dictionaryMultiname;
handlerMappingsTrait.vindex = 0;
handlerMappingsTrait.vkind = null;
instance.traits.push(handlerMappingsTrait);
...

On Mar 24, 2:45 am, "maxim.porges" <maxim.por...@gmail.com> wrote:

maxim.porges

unread,
Apr 7, 2009, 1:34:20 AM4/7/09
to loom-as3
Nice! Opcodes are working too... I just weaved in the "setHandler"
method along with opcodes to trace a message from Loom. describeType()
output and Loom API code for weaving the method are listed below.
Clearly the API needs some usability work, but it's getting the job
done.

<type name="loom.template::BaseClass" base="Class" isDynamic="true"
isFinal="true" isStatic="true">
<extendsClass type="Class"/>
<extendsClass type="Object"/>
<accessor name="prototype" access="readonly" type="*"
declaredBy="Class"/>
<factory type="loom.template::BaseClass">
<extendsClass type="Object"/>
<constructor>
<parameter index="1" type="*" optional="false"/>
<parameter index="2" type="*" optional="false"/>
</constructor>
<method name="methodCallOne" declaredBy="loom.template::BaseClass"
returnType="int">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="setHandler" declaredBy="loom.template::BaseClass"
returnType="void" uri="http://loom.ninjitsoft.com">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Function" optional="false"/>
</method>
<method name="methodCallThree"
declaredBy="loom.template::BaseClass" returnType="String">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="methodCallTwo" declaredBy="loom.template::BaseClass"
returnType="void">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
<parameter index="3" type="Object" optional="false"/>
</method>
<variable name="handlerMappings" type="flash.utils::Dictionary"
uri="http://loom.ninjitsoft.com"/>
</factory>
</type>

// Add the setHandler method info
var setHandlerMethodInfo : MethodInfo = new MethodInfo();
setHandlerMethodInfo.loomName = setHandlerQName;
setHandlerMethodInfo.methodName = "";
setHandlerMethodInfo.argumentCollection.push(new Argument
(QualifiedName.STRING));
setHandlerMethodInfo.argumentCollection.push(new Argument
(QualifiedName.FUNCTION));
setHandlerMethodInfo.returnType = QualifiedName.VOID;
abcFile.methodInfo.push(setHandlerMethodInfo);

// Add the setHandler method trait
var setHandlerTrait : MethodTrait = new MethodTrait();
setHandlerTrait.traitMultiname = setHandlerQName;
setHandlerTrait.traitKind = TraitKind.METHOD;
setHandlerTrait.isOverride = false;
setHandlerTrait.dispositionId = 0;
setHandlerTrait.traitMethod = setHandlerMethodInfo;
instance.traits.push(setHandlerTrait);

// Add the setHandler method body
var setHandlerBody : MethodBody = new MethodBody();
setHandlerBody.initScopeDepth = 4;
setHandlerBody.localCount = 4;
setHandlerBody.maxScopeDepth = 5;
setHandlerBody.maxStack = 2;
setHandlerBody.methodSignature = setHandlerMethodInfo;

var traceMultinamePosition : int =
abcFile.constantPool.getMultinamePositionByName("trace");
var message : String = "This is a message woven in by Loom!";
abcFile.constantPool.addString(message);
setHandlerBody.opcodes = [
Opcode.getlocal_0.op(),
Opcode.pushscope.op(),
Opcode.findpropstrict.op([abcFile.constantPool.multinamePool
[traceMultinamePosition]]),
Opcode.pushstring.op([message]),
Opcode.callproperty.op([1, abcFile.constantPool.multinamePool
[traceMultinamePosition]]),
Opcode.pop.op(),
Opcode.returnvoid.op()
];
abcFile.methodBodies.push(setHandlerBody);

Here's the code for loading the class ref and executing the method
call (still asynchronous since I haven't written the SWF enhancer
yet).

var classLoader : AbcClassLoader = new AbcClassLoader();
classLoader.addEventListener(
Event.COMPLETE,
function (event : Event) : void
{
var classRef : * = getDefinitionByName
("loom.template::BaseClass");
trace(describeType(classRef));
var instance : * = new classRef(null, null);
instance.loom_namespace::handlerMappings = new Dictionary();
instance.loom_namespace::handlerMappings["hello"] = "Hello
Loom!";
trace(instance.loom_namespace::handlerMappings["hello"]);
instance.loom_namespace::setHandler(null, null);
}
);
classLoader.loadClassDefinitionsFromBytecode([
new AbcSerializer().serializeAbcFile(proxyFile)
]);

Drew Bourne

unread,
Apr 7, 2009, 1:46:38 AM4/7/09
to loom...@googlegroups.com
Looking good Max, \o/

cheers,
Drew

maxim.porges

unread,
Apr 9, 2009, 12:48:27 AM4/9/09
to loom-as3
OK guys, final stretch... after a little tinkering tonight, I just
passed a loom.template:BaseClass in to the DynamicProxyFactory and got
a Class back that produced the following describeType() output. Note
the superclass hierarchy...

<type name="loom.template::DynamicSubClass" base="Class"
isDynamic="true" isFinal="true" isStatic="true">
<extendsClass type="Class"/>
<extendsClass type="Object"/>
<accessor name="prototype" access="readonly" type="*"
declaredBy="Class"/>
<factory type="loom.template::DynamicSubClass">
<extendsClass type="loom.template::BaseClass"/>
<extendsClass type="Object"/>
<constructor>
<parameter index="1" type="*" optional="false"/>
<parameter index="2" type="*" optional="false"/>
</constructor>
<method name="methodCallOne"
declaredBy="loom.template::DynamicSubClass" returnType="int">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="setHandler"
declaredBy="loom.template::DynamicSubClass" returnType="void"
uri="http://loom.ninjitsoft.com">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Function" optional="false"/>
</method>
<method name="methodCallThree"
declaredBy="loom.template::DynamicSubClass" returnType="String">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<method name="methodCallTwo"
declaredBy="loom.template::DynamicSubClass" returnType="void">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
<parameter index="3" type="Object" optional="false"/>
</method>
<method name="proxyInvocation"
declaredBy="loom.template::DynamicSubClass" returnType="*" uri="http://
loom.ninjitsoft.com">
<parameter index="1" type="loom.template::MethodInvocation"
optional="false"/>
</method>
<variable name="handlerMappings" type="flash.utils::Dictionary"
uri="http://loom.ninjitsoft.com"/>
</factory>
</type>

What was really cool was when I new'd the Class, the opcodes fired and
called the superclass constructor in loom.template:BaseClass! :)

I have just one experimental item left, which is to stomp the opcodes
in methodCallOne/Two/Three and replace them with the opcodes to invoke
proxyInvocation(). Once that's working, there's some serious cleanup
required in DynamicProxyFactory since I hard-coded a lot of values
while I was getting me head around the process, but after that's
cleaned up and I've validated the ability to proxy more than just
loom.template:BaseClass, I'll send out the alpha.

Thanks,

- max
> ...
>
> read more »

Brian Kotek

unread,
Apr 9, 2009, 10:12:44 AM4/9/09
to loom...@googlegroups.com
Sweet, it sounds like it's coming along very nicely. Thanks again for your effort on this, Max!

Brian
Reply all
Reply to author
Forward
0 new messages