16
ColdFusion arguments.callee
12 Comments | Posted by Elliott in Adobe, ColdFusion, Programming
Last night Ben Nadel sent me an email asking if there was any way to get the currently executing function so you could get the metadata from it.
<cffunction name="test" myAttribute="1"> <--- How can we get the myAttribute value? ---> </cffunction>
The first obvious attempt at this is to use getMetaData(test).myAttribute, and that works fine until you pass the function as a pointer and then it’s not called test anymore. Instead we need a different way to get the current function.
I had looked into this for implementing closures some time ago, and even talked about this at BFusion 2008. My original workaround was to create an API around the function pointer. For example executeFunction(func) that passes the function pointer in as an argument. Unfotunately, this also means you can’t just pass this function around transparently to anything that expects a function pointer.
Last night, however, I had a eureka moment and figured this one out. To get a reference to the current function we’re going to harness exceptions and the information we can get from the stack.
<cffunction name="getStackFunction" access="public" returntype="any" output="false">
<cfargument name="name" type="string" required="true">
<cfargument name="depth" type="numeric" required="false" default="1">
<cfset var TemplateClassLoader = createObject("java","coldfusion.runtime.TemplateClassLoader")>
<cfset var servletContext = getPageContext().getServletContext()>
<cfset var templatePath = "">
<cfset var TemplateClass = "">
<cfset var field = "">
<cftry>
<cfthrow type="Exception">
<cfcatch type="any">
<cfset templatePath = cfcatch.tagContext[depth+1].template>
</cfcatch>
</cftry>
<cfset TemplateClass = TemplateClassLoader.findClass(servletContext,templatePath)>
<cfset field = TemplateClass.getDeclaredField(arguments.name)>
<cfset field.setAccessible(true)>
<cfreturn field.get(TemplateClass.newInstance())>
</cffunction>
We can then use this code with the (case sensitive) name of the function in the current stack to get a pointer to that function.
<cffunction name="test" myAttribute="1" output="false">
<cfargument name="x" type="numeric">
<cfset arguments.callee = getStackFunction("test")>
<cfreturn arguments.x + getMetaData(arguments.callee).myAttribute>
</cffunction>
You can use the depth parameter to get functions farther down the stack as well.
I’ll go over how to build proper closure constructs using this technique, and some other novel ones that don’t even require runtime magic in an upcoming post.
Oh, and hats off to Ben for sparking my interest in this issue again! :)
12 Comments for ColdFusion arguments.callee
Steve Bryant | July 16, 2009 at 11:33 pm
Adam Cameron | July 17, 2009 at 4:38 am
Hi Elliott
This seems rather similar (although more complicated) than a UDF I mentioned to you in “another forum” a few months back. Good to see a slightly different technique though.
It’s probably also worth while pointing people to the issue you raised with Adobe on this one, now that the bugbase is public:
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=73010
It’d be good to get more votes.
–
Adam
Adam Cameron | July 17, 2009 at 7:57 am
Yep, as per other discussion, you’re dead right. Sorry for the confusion (albeit it’s mostly on my part, it seems ;-)
Cheers.
–
Adam
Ben Nadel | July 17, 2009 at 8:03 am
Elliott,
Very interesting stuff! I failed after 2 hours and I’m satisfied in saying that I would NEVER have come up with this at all :D Nice work!
Adam Cameron | July 17, 2009 at 10:51 am
Nope: no blog, me. Well I do, but not a technical one. I keep thinking about it, but I so seldom have anything worthwhile saying, I don’t see the point.
Most of the things I think of to say stem from what I read on other people’s blogs, so I just stick my oar in as-and-when. Not that today’s effort was a shining example: I’m usually better than that!
–
Adam
aMeen | July 18, 2009 at 3:32 am
Got Lost … I gave up
:(
Ben Nadel | July 21, 2009 at 9:54 am
I can’t seem to get this to work if the function pointer is in another scope such as:
At this point, it looks like “test” is not a declared field in the template taken from the exception tag context.
Thoughts??
Sean | February 9, 2010 at 4:06 pm
This got me part the way:
thisCFCPage = GetPageContext().getFusionContext().getPagePath();
Here’s what’s sad: There is this gem:
GetPageContext().getFusionContext().methodCalledName
but it’s a private property and there is no getter function. ARRRGGGHHHHH!!!!!!
Leave a comment!
Wrap code examples in <code> tags to prevent stripping of tags.

I have been thinking about this problem recently as well so it is nice to see a solution. Somehow, though, using exception handling just seems… wrong. How is the performance on that?
I was thinking of implementing a method that uses the named arguments of the method that calls it (long story). Basically, I want to be able to programmatically get a list of the arguments for a method. I could pass in This and the method name, but I wanted something with a cleaner API.
Anyway, this is really great to see and I am eager to see what else you do with this.