Menu

As simple as possible, as complex as necessary

Migrating from ColdFusion to Railo Part 4: Null and function arguments

5 December 2014

Continuing my series of notes on the nitty gritty of migrating from ColdFusion 9 to Railo 4.2.

← Migrating from ColdFusion to Railo Part 3

ColdFusion's null handling has been described as incomplete and inconsistent, but Railo has problems of its own thanks to an apparent bug in the way function arguments are processed under certain circumstances.

16 December 2014 Both problems described below have been fixed in "preview" patch release 4.2.2.004. See issue 3274 in the Railo bug tracker

The issue

To illustrate, let's first create an object and define a property with no default value, i.e. one which will return an "undefined" or null value when accessed.

Object.cfc

component accessors="true"{
	
	property name="propertyWithUndefinedValue";

	public function init(){
		return this;
	}

}

Now we'll create a function that takes a single optional argument with a default value.

function test( arg="default" ){
	WriteDump( arg );
}

Let's instantiate our object and pass its property to the function.

o=New Object();
test( o.getPropertyWithUndefinedValue() );

Results: CF9 dumps default, whereas Railo throws an exception "variable [arg] doesn't exist".

CF9 treats the null value as if we hadn't passed an argument at all (so gives the default). Railo on the other hand seems to sense the argument's been passed (since the default value isn't given) but it doesn't know how to deal with the reference to it.

Workaround?

I've found the exception can be avoided in Railo in two ways.

1) Fully scope the argument reference inside the function:

function test( arg="default" ){
	WriteDump( arguments.arg );
}

However, the result will now be Empty:null. This is arguably the "correct" response, since that is indeed the value we have passed. But why doesn't that happen with the unscoped reference? I personally prefer the readability of unscoped variables when it is safe to use them, which it generally is in the case of the arguments scope. Explicitly scoping all my argument variables isn't something I want to be forced to do.

2) The second approach is to use named rather than positional arguments when calling the function:

test( arg=o.getPropertyWithUndefinedValue() );

The result will then be the same as ACF: default.

But in my code base positional arguments are the norm unless there are more than two or three. Changing them all to named isn't an option I'd be happy with.

Full null support?

So far I've been discussing Railo's behaviour with the "Null Support" Language/Compiler switch set to "Partial (CFML Default)". In this mode, Railo should generally replicate ACF's null handling, warts and all - although as we've seen it seems to have added it's own wart.

The switch can however be set to "Complete support" meaning nulls are treated as legitimate values, interpreted consistently as such and settable with the literal null.

With full null support on, our original call results in the "correct" output we got earlier using explicit scoping.

function test( arg="default" ){
	WriteDump( arg );
}
test( o.getPropertyWithUndefinedValue() );

Result: Empty:null.

Although different from CF9's handling, this proper way of dealing with nulls is very appealing and would probably work with most of my own code.

However, having switched to full null support I soon discovered a major downside.

StructKeyExists( arguments,"arg" ) Named arguments bug

The following function takes two optional arguments and should tell us whether the second argument was passed.

function wasTheSecondArgPassed( arg1,arg2 ){
	WriteDump( StructKeyExists( arguments,"arg2" ) );
}

Now, with Railo's full null support on, let's pass in just one argument using first positional and then named arguments. The answer should be "false" in both cases.

wasTheSecondArgPassed( "x" );
wasTheSecondArgPassed( arg1="x" );

Result:

false
true

Eh? This time it's the named argument approach which fails!

Issue 2415 in the bug tracker, raised over a year and a half ago and unresolved, suggests why this may be happening. More recently Issue 3098 describes the consequences.

Which are serious, as I found when I attempted to use the spreadsheet extension I'd so painstakingly installed. It no longer worked due to incorrect results from the numerous StructKeyExists() calls to check which arguments were being passed. The embedded (and ubiquitous) JavaLoader library also failed for the same reason.

What to do?

At this point I'm not sure of the best approach. I'd dearly like to go with full null support, and adjusting the small number of StructKeyExists() calls I'm likely to find in my own functions wouldn't be too much of a price to pay. But doing the same for third party libraries and code in which this usage seems to be very common? I'm not sure I that's something I'm prepared to take on for the sake of a bug.

Thoughts, advice, solutions... any and all appreciated.

Migrating from ColdFusion to Railo Part 5: Scope names and local variables →

Posted on . Updated

Comments

  • Formatting comments: See this list of formatting tags you can use in your comments.
  • Want to paste code? Enclose within <pre><code> tags for syntax higlighting and better formatting and if possible use script. If your code includes "self-closing" tags, such as <cfargument>, you must add an explicit closing tag, otherwise it is likely to be mangled by the Disqus parser.
Back to the top