Static scope corruption caused by inheritance

Description

Given the following components:

//base.lucee class { static { public member = "base"; } } // child.lucee class extends=base { static { public member = "child"; } } // sibling.lucee class extends=base { static { public member = "sibling"; } }

It does weird things:

//index.cfm dump(Base::member); // expect "base", get "base" dump(Child::member); // expect "child", get "child" dump(Base::member); // expect "base", get "child" dump(Sibling::member); // expect "sibling", get "sibling" dump(Child::member); // expect "child", get "sibling" dump(Base::member); // expect "base", get "sibling"

It's acting as though all these classes are sharing the same instance of StaticScope.

Environment

None

relates to

Activity

Michael Offner 
9 May 2016 at 19:13

this commit:
https://github.com/lucee/Lucee/commit/13171c8ad70ac8f11613881f8ea3b510ea0432d1

only change the behavior inside the static constructor (safe always in current static scope only), but i did also start the discussion on this with TAG.
In every case it solves your issue, but maybe we will change also the behavior outside the static constructor in the same way.

Stay tuned.

Michael Offner 
9 May 2016 at 18:58

"super" points to the THIS scope of the base component, so the base instance, not the component representation.
So using "super" to address the static scope of the base component is wrong in my opinon.

We will discuss this and come with a decision asap.

Jesse Shaffer 
9 May 2016 at 15:40
(edited)

Thanks for investigating this. I understand the thought process a bit better now.

So one solution could be that when you write a variable it is always written to the current component static scope and if you wish to write to the base component, you have to address the component explicitly, like this:

MyBaseComp::member="base";

But i don't think this is a good idea.

I get where you're coming from, but honestly, that is the solution that makes the most sense. Writes should always go to the nearest scope, and reads should look at the nearest first, then go up the chain until it is found. That's one of the major problems you had compatibility-wise with the variables/arguments/local scope chain. With the "modern" settings, variables go into local scope first, whereas they used to go into variables scope first. It just feels more logical to always write to the nearest scope, and let reads bubble up.

I don't know if it is already possible, but it might also make sense to use `super::` to access static members in a parent class, so you do not have to specify the class name.

Michael Offner 
9 May 2016 at 15:10

A base and child component do not share a single static scope, but every static scope has a reference to the static scope of the base component and if you write a variable to the static scope, the static scope first checks if the base component static scope already has this variable. if so it updates the variable in the static scope of the base component instead of setting the variable in the current static scope. So this 3 components share a single variable, but not the same static scope. You can see that when you dump the static scope and you add specific variables to every component, you will not see the variables of sibling components.

This process is perfectly fine for reading a variable, first we check the child and then the base for a static variable.
But what should happen if we write/remove a variable, maybe we wish to write to the base component variable and not create a new variable in the current static scope, so how we decide what to do.

in Java for example this is very easy, because i have explicitly to declare a variable, so as soon i declare a static variable in the current class, i only interact with that variable, if not i'm interacting with the base class variable.
Of course this is impossible with CFML because CFML does not need the step to declare a variable.

So one solution could be that when you write a variable it is always written to the current component static scope and if you wish to write to the base component, you have to address the component explicitly, like this:

MyBaseComp::member="base";

But i don't think this is a good idea.

In my opinion the best solution is that if you set a variable in the static constructor, it is always set in the current static scope, but if you set a variable outside the static constructor, lucee will still follow the old pattern.

Jesse Shaffer 
1 May 2016 at 11:57

This is on 235-RC

Fixed

Details

Assignee

Reporter

Priority

Labels

Fix versions

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

Created 1 May 2016 at 11:55
Updated 5 May 2023 at 09:41
Resolved 10 August 2016 at 15:40