Skip to main content
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Sam Effa
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Sam Effa

Two Spread Operator Bugs In Adobe ColdFusion 2023

By
Published in Comments (1)

I ran into two strange bugs this morning when attempting to use the Spread operator in Adobe ColdFusion (2021 and 2023). One bug prevents the CFML template from compiling; the other bug is just a completely nonsense outcome of the spread operation (at least compared to JavaScript).

Template Compilation Issue

The first bug is that ColdFusion won't compile the CFML template if you attempt to use the spread operator as part of an expression that uses bracket notation to reference properties. Consider the following ColdFusion code:

<cfscript>

	logLevels = {
		OFF:   { level: 0 },
		FATAL: { level: 100 },
		ERROR: { level: 200 },
		WARN:  { level: 300 },
		INFO:  { level: 400 },
		DEBUG: { level: 500 },
		TRACE: { level: 600 }
	};

	// Attempt to SPREAD the OFF properties into a new object.
	selectedLevel = {
		...logLevels[ "OFF" ]
	};

	writeDump( selectedLevel );

</cfscript>

In this case, I'm using bracket notation, ["OFF"], to reference the object that I am trying to spread into another object literal. If we run this ColdFusion code we get the following error:

The CFML compiler encountered an unexpected coldfusion compiler CompilerInternalException exception. The reason for this was: Unable to complete CFML to Java translation.

Nonsense Result Issue

You can get around the above issue (in this case) by simply using the . property access instead of the [] property access. However, attempting to do this reveals another strange behavior. Consider this version of the ColdFusion code:

<cfscript>

	logLevels = {
		OFF:   { level: 0 },
		FATAL: { level: 100 },
		ERROR: { level: 200 },
		WARN:  { level: 300 },
		INFO:  { level: 400 },
		DEBUG: { level: 500 },
		TRACE: { level: 600 }
	};

	// Attempt to SPREAD the OFF properties into a new object.
	selectedLevel = {
		...logLevels.OFF
	};

	writeDump( selectedLevel );

</cfscript>

This ColdFusion code runs; but, I have no idea what it's actually doing. At first, I thought it was an operator precedence issue leading to a strange order of operations; but, the more I look at the outcome, the less sense it makes.

Consider the possible order of operations. An expression like this would either execute as such (I'm adding parenthesis to show order of execution):

( ... logLevels ) . OFF

Or, it would execute like this:

... ( logLevels . OFF )

In the former case, you would end up with a new Struct that contains an .OFF property. In the latter case, you would end up with a new Struct that contains a .level property. ColdFusion does neither of these things:

The outcome of the spread operator in both ColdFusion and JavaScript.

Somehow, ColdFusion manages to put a logLevels property in the new object. There's no order of operations where that makes sense. Comparatively, JavaScript, creates a new object with just the .level property. It does this because JavaScript gives the ... operator the lowest possible precedence

You can get around both of these bugs by storing the spreadable object into an intermediary variable:

<cfscript>

	logLevels = {
		OFF:   { level: 0 },
		FATAL: { level: 100 },
		ERROR: { level: 200 },
		WARN:  { level: 300 },
		INFO:  { level: 400 },
		DEBUG: { level: 500 },
		TRACE: { level: 600 }
	};

	// Use an intermediary variable so as not to confuse the CFML compiler.
	targetLevel = logLevels.OFF;

	// Attempt to SPREAD the OFF properties into a new object.
	selectedLevel = {
		...targetLevel
	};

	writeDump( selectedLevel );

</cfscript>

When using the ... operator with a naked variable, ColdFusion does what you expect it to. And, we end up with a new object that has a .level property.

I will open two separate bug tickets and leave them in the comments.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel