Cannot set calling scope vars from closures when localMode="modern"

Description

Consider this code:

<cfscript> function doSomething ( any onComplete) { writeOutput("<p>I just did something!</p>"); onComplete(); } function test () { var myVar = ""; writeOutput("<p>Before closure: [#myVar#]</p>"); doSomething(function () { myVar = "test"; }); writeOutput("<p>After closure: [#myVar#]</p>"); } test(); </cfscript>

In application.cfc, when this.localMode="modern", the test fails to persist the change made to myVar. When turned off, it works as expected.

Environment

None

Activity

Adam Cameron 
7 June 2021 at 17:10

Just in case this helps, here’s a slight variation that was biting me (from https://gist.github.com/adamcameron/6d845b09bbe1c2c7b9383a3f43d6dbe9 )

 

<cfscript> function f() { var one = "tahi" var red = "whero" setInVariablesScope({one=one, red=red}) } function setInVariablesScope(locals) { locals.each((key, value) => { variables[key] = value }) } f() writeOutput(variables.keyExists("one") ? variables.one : "one not set in variables") writeOutput("<br>") writeOutput(variables.keyExists("red") ? variables.red : "red not set in variables") </cfscript>

Lucee:

 

one not set in variables red not set in variables

ColdFusion 2021:

tahi whero

Somewhat contradicting 's earlier comment that “…the local scope is always prefered…”, the code above can be sorted out by using an intermediary variable:

 

function setInVariablesScope(locals) { vars = variables locals.each((key, value) => { vars[key] = value }) }

So Lucee’s closing over vars just fine. Just not variables.

Zac Spitzer 
7 June 2021 at 15:21
(edited)

<cfscript>     function outer() {         local.a=1;         var zzz = function () {             variables.a=2;             variables.b=2;         };         zzz();         writedump(local);         writedump(variables);     }     outer(); </cfscript>

In Lucee the variable scope prefix on lines 5 & 6 has no effect

https://github.com/lucee/Lucee/blob/6.0/core/src/main/java/lucee/runtime/type/scope/ClosureScope.java#L222

JP 
21 May 2021 at 22:04

what I don’t like about the workaround is the additional programming bits for something that should just work, no matter what mode you’re in. To me those kinds of directives seem problematic from a code maintenance standpoint. Anyone who looks at that code 5 years from now will be scratching their heads.

Matthew Clemente 
21 May 2021 at 21:42

Thanks

I do see that override working on my example function - here’s the syntax: function example() localmode = "classic" {

Interestingly, it doesn’t work if I put the override on the anonymous function within the each(), I guess because the parent function there is still operating with localMode="modern".

Agreed that it’s a less than ideal workaround. Ideally, I think, the local scope would be preferred, but I there would be a syntax to explicitly modify parent scope variables, such as explicitly scoping to variables.

JP 
21 May 2021 at 21:26

afaik you have to explicitly override the “modern” setting in your closure function definition by adding localmode='classic' to the definition. It’s a terrible workaround but I believe it works. I have not tried it and I’m not entirely sure what the syntax is, but from what I’ve been told it works. I just turned off modern localmode instead of dealing with it. I only was using modern localmode as a fallback to code mistakes when a variable was inintentionally not declared.

Details

Assignee

Reporter

Priority

New Issue warning screen

Before you create a new Issue, please post to the mailing list first https://dev.lucee.org

Once the issue has been verified, one of the Lucee team will ask you to file an issue

Affects versions

Created 4 April 2019 at 15:51
Updated 7 June 2021 at 17:10