ColdFusion cyclic references and ERROR 500

Friday, 06 March 2009 11:58 Javier Albinarrate
Print


Recently I briefly went back to program in ColdFusion, after 8 years or so of not doing so. I just had to add a function to existing code, which would perform very simple steps, and call a bunch of pre-existing functions. Quite simple right?
So I followed the more or less regular steps of the other functions and modified that to suit my needs.
When I started testing I suddenly find out that this was not going to be easy.
I got the ugly error "500 Event Handler Exception" which basically is CF saying "Hey!, Something went awfully wrong and I have no idea what was it". With a lot of helpful information like "Error: null" and "The error occurred on line -1".

Browsing internet for the error is pretty much useless.
I spent some minutes trying to identify the single line that was causing it, and it was a simple call to one of the other functions.
It also took me a few hours to identify WHY that was hapening (and a final help from a colleague helped), but you are lucky today and I will provide the answer directly.

See the following simplified example code:

 <cfset X = StructNew()>
 <cfset X.Y = 1>
 <cfset X.Z = X>
 <cfoutput>
  X.Y   = #X.Y#   <br/>
  X.Z.Y = #X.Z.Y# <br/>
 </cfoutput>
 This is a killer:
 <cfdump var="#X#">
 

If you create a cyclic reference, something perfectly reasonable sometimes, CF can create it without trouble and you can use single values or branches without trouble. But... don't even dream of looping the struct.
A CFDUMP, or any serialization will send you directly to Error 500.

Now, in theory or in this example, the fact that you are creating a cyclic reference is simply obvious.
But when dealing with code, whoever programs the code, it might be easy that one function, using the variables scope, interferes with another function.

Example:

<cfset result = fnB()>
This is a killer:<br/>
<cfdump var="#variables.X#">
 
<cffunction name="fnB">
 <cfset X = StructNew()>
 <cfset X.A = fnA()>
 <cfoutput>
  X.y   = #X.y#   <br/>
  X.A.y = #X.A.y# <br/>
 </cfoutput>
 <cfreturn X>
</cffunction>

 <cffunction name="fnA">
  <cfset X = StructNew()>
  <cfset X.y = 1>
  <cfreturn X>
 </cffunction>

 

Remember that the default scope for variables in CF (when you use no prefix and no var statement) is the "variables." scope. And this means that a variable is available only on the page on which it is created and any included pages. So different functions in the same page will share the same "variables." scope, so if you use it instead of the function "local" scope (when you use the var statement), this calls for trouble.

In this example, both functions were all the time referring to the same "variables.X", thus a recursive reference was created inadvertently (again, remember that the example is a simplification, and that the reference was not really obvious at first sight in the real world case).

This could have been solved in this way:

<cfset X = StructNew()>
<cfset result = fnB()>
This is NOT a killer:<br/>
<cfdump var="#variables.X#">
 
<cffunction name="fnB">
 <cfset var X = StructNew()>
 <cfset X.A = fnA()>
 <cfset X.y = 2>
 <cfoutput>
  X.y   = #X.y#   <br/>
  X.A.y = #X.A.y# <br/>
 </cfoutput>
 <cfreturn X>
</cffunction>

 <cffunction name="fnA">
  <cfset var X = StructNew()>
  <cfset X.y = 1>
  <cfreturn X>
 </cffunction>

From the point of view of the language, it is a shame that CF doesn't realize of cyclic references while looping them, or at least to fail gracefully with some information on the error.

From the point of view of the programmer, this could have been avoided if the functions would have used correct scopes with var statement.

This leads me to the morals of this tale:

 

 

Last Updated on Monday, 27 July 2009 14:36