Bug with ColdFusion 11's Elvis Operator

This post is more than 2 years old.

Ok, snap quiz time, given the following code, and that url.name exists, will the UDF run?

function getfoo() {
	writeoutput("do you see me?");
	return "foo";	
}

u2 = encodeForHTML(url.name) ?: getfoo();

Ok, time's up. If you said it wouldn't run, because, of course, it doesn't need to, then you would be wrong. Even when url.name exists, getfoo() is executed. Now, in some ways, this is consistent with cfparam's behavior. But to me, that always made sense. When I see this: <cfparam name="url.name" default="#getfoo()#"> - I always figured the compiler had to run getfoo to get the value to cfparam so cfparam could then work. Maybe you don't agree - and that's cool - but it makes sense to me.

In the case of the Elvis operator though it does not make sense. As an FYI, both Railo and Groovy ignore the right hand side when they don't need it.

Bug: 3818770

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Adam Cameron posted on 9/5/2014 at 12:07 PM

The thing with CFPARAM does make sense after a fashion, but it still catches people out.

I do think when the CFML is being compiled it should be compiled down in such a way that the default attribute value is *not* executed unless it's needed, but I see how they've taken a generic approach to processing tags, which does mean all values need to be resolved before they're passed into the tag's actual code. Tag attributes are basically function arguments.

However there's no way that that quirk of how *tags* need to work should filter down into the ?: implementation. That's just wrong.

Cheers for raising the bug, Ray.

--
Adam

Comment 2 by Adam Cameron posted on 9/5/2014 at 5:09 PM

As per Twitter... Matt Busche noticed that you've got yer () misplaced in that sample. This:

u2 = encodeForHTML(url.name) ?: getfoo();

Should be this:

u2 = encodeForHTML(url.name ?: getfoo());

But your point still stands.That's just a typo.

--
Adam

Comment 3 by Raymond Camden posted on 9/5/2014 at 5:10 PM

Why though? I'm using encodeForHTML because I don't trust url.name. I trust the result of getfoo to be safe for already.

Comment 4 by Raymond Camden posted on 9/5/2014 at 5:11 PM

(to be safe to display I mean)

Comment 5 by Adam Cameron posted on 9/5/2014 at 5:13 PM

Yeah, but this will fail if url.name doesn't exist:

encodeForHTML(url.name)

And... it does fail. I'm not just making this up.

--
Adam

Comment 6 by Raymond Camden posted on 9/5/2014 at 5:13 PM

Weird - I didn't see that. Testing again.

Comment 7 by Raymond Camden posted on 9/5/2014 at 5:15 PM

No, I don't get an error. Just to be precise, here is a gist of what I'm running.

https://gist.github.com/cfj...

i test with and without name being defined in the QS and I never get a runtime error. (I do get the bug with RHS of course.)

Comment 8 by Adam Cameron posted on 9/5/2014 at 5:51 PM

Yeah, as per IRC discussion... you done found ANOTHER bug with ?: on CF (I was testing on Railo, which works properly). For the sake of others reading along, THIS should error:

encodeForHTML(url.name)

when url.name doesn't exist. So it doesn't matter that the next part of the expression has a ?:, because the code should already have errored out.

--
Adam

Comment 9 by Raymond Camden posted on 9/5/2014 at 5:59 PM

Adam, I thought you said it was a bug with encodeForHTML?

Comment 10 by Timothy Farrar posted on 9/5/2014 at 10:13 PM

Run the following code... pretty sad!
Not only does it run the second one first.
<cfscript>
function a(){
writeDump('a');
return 'a';
}

function b(){
writeDump('b');
return 'b';
}
test = a() ?: b();
</cfscript>

Comment 11 by Raymond Camden posted on 9/5/2014 at 10:15 PM

@Timothy: I'm confused - isn't this just what the blog said? Are you trying to show something different?

Comment 12 by Timothy Farrar posted on 9/5/2014 at 10:20 PM

My point was that it not only always runs both sides, but that it runs the right hand side before the left hand side.

Comment 13 by Timothy Farrar posted on 9/5/2014 at 10:22 PM

Perhaps this is what you were showing in your blog post, but I missed that. With the above code, I get 'b a' as output. I would have expected at least 'a b' to come back.

Comment 14 by Raymond Camden posted on 9/12/2014 at 7:04 PM

The bug is now fixed.