Hey guys,
Here it is, after just under three months of work - the long-awaited working DynamicProxyFactory. Please find the quick n' dirty test case and output from it below.
The original methodOne() implementation in BaseClass just traces "BaseClass::methodCallOne()" and returns the value 100, and you can see the output in the first non-proxied invocation. Next, the proxy handler is attached for methodCallOne() and invoked against the original instance, and you can see the before/after tracing advice working away.
Now that this is all working, I'm going to be writing some more thorough unit tests for DynamicProxyFactory and then refactoring it heavily. Once DynamicProxyFactory can create a proxy from any class, I'll get started on the SWFLoader. Regardless of where I get to today, I'll send out a copy of the project later so you guys can start messing with creating proxies against instances of BaseClass at the very least. Then you can let me know what else you need from the API and any other ideas that start cropping up.
Thanks,
- max
======== Test Class ===
public class DynamicProxyFactoryTest extends TestCase
{
public function testCreateProxy() : void
{
var fixture : DynamicProxyFactory = new DynamicProxyFactory();
var proxyFile : AbcFile = new AbcDeserializer(TestConstants.getBaseClassTemplate()).deserialize();
// We enhance the loaded ABC file to save the work the compiler already did, and just add to/replace as necessary
fixture.createProxy(proxyFile);
var classLoader : AbcClassLoader = new AbcClassLoader();
classLoader.addEventListener(
Event.COMPLETE,
function (event : Event) : void
{
var classRef : * = getDefinitionByName("loom.template::DynamicSubClass");
trace(describeType(classRef));
// This would blow up if the types were not compatible
var instance : BaseClass = new classRef(null, null);
assertTrue(instance is BaseClass); // redundant, yet satisfying :)
// Run original method invocation
trace(">>> Original method invocation");
trace(instance.methodCallOne("Hello Original Method Invocation!", -1));
// Set up a handler
instance.loom_namespace::setHandler(
"methodCallOne",
function (invocation : MethodInvocation) : *
{
trace("Before advice for: " + invocation);
try
{
return invocation.proceed();
}
catch (e : Error)
{
trace("Throws advice for: " + e);
}
finally
{
trace("After advice for: " + invocation);
}
}
);
trace(">>> AOP method invocation");
trace(instance.methodCallOne("Hello Dynamic Method Invocation!", 1234));
}
);
// Load in the proxied class definition
classLoader.loadClassDefinitionsFromBytecode([
TestConstants.getBaseClassTemplate(),
new AbcSerializer().serializeAbcFile(proxyFile)
]);
}
}
======== Output ===
<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="String" optional="false"/>
<parameter index="2" type="String" optional="false"/>
</constructor>
<method name="methodCallThree" declaredBy="loom.template::DynamicSubClass" returnType="String">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Number" optional="false"/>
</method>
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Function" optional="false"/>
</method>
<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="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>
<parameter index="1" type="loom.template::MethodInvocation" optional="false"/>
</method>
</factory>
</type>
BaseClass::constructor()
>>> Original method invocation
BaseClass::methodCallOne()
100
>>> AOP method invocation
Before advice for: methodCallOne(Hello Dynamic Method Invocation!, 1234)
BaseClass::methodCallOne()
After advice for: methodCallOne(Hello Dynamic Method Invocation!, 1234)
100