Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make FontData key in map of ScalingSWTFontRegistry immutable #218

Closed
fedejeanne opened this issue Jan 30, 2025 · 6 comments · Fixed by eclipse-platform/eclipse.platform.swt#1994
Assignees
Labels
HiDPI A HiDPI-Related Issue or Feature SWT Issue for SWT
Milestone

Comments

@fedejeanne
Copy link

fedejeanne commented Jan 30, 2025

As mentioned in point Nr. 3 of #98 (comment) (pasted below), this could lead to problems if someone changes the internal state of fontData.

3. org.eclipse.swt.internal.ScalingSWTFontRegistry.fontKeyMap uses FontData as key

(Same problem for org.eclipse.swt.internal.DefaultSWTFontRegistry.fontsMap).

FontData is not immutable which may lead to information getting lost.

Example

public static void main(String[] args) {
  Map<FontData, String> map = new HashMap<>();

  FontData fontData = new FontData("f1", 10, 10);
  map.put(fontData, "found!");

  System.out.println(map.get(fontData));

  fontData.height += 1;

  System.out.println(map.get(fontData));
}

Produces the output:

found!
null

Task

Make the key in the registry map immutable. Possible options are:

  • Clone the FontData used as key so that no one outside the registry has access to it and may modify it
  • Find some other kind of key that is both unique and immutable
@fedejeanne fedejeanne added this to HiDPI Jan 30, 2025
@fedejeanne fedejeanne added SWT Issue for SWT HiDPI A HiDPI-Related Issue or Feature labels Jan 31, 2025
@fedejeanne fedejeanne moved this to 🆕 New in HiDPI Jan 31, 2025
@HeikoKlare HeikoKlare added this to the 4.36 M1 milestone Feb 3, 2025
@fedejeanne fedejeanne moved this from 🆕 New to 🔖 Ready: Atomic in HiDPI Feb 3, 2025
@fedejeanne fedejeanne removed their assignment Feb 3, 2025
@HeikoKlare HeikoKlare changed the title org.eclipse.swt.internal.ScalingSWTFontRegistry.fontKeyMap uses FontData as key Make FontData key in map of ScalingSWTFontRegistry immutable Feb 24, 2025
@ShahzaibIbrahim ShahzaibIbrahim self-assigned this Apr 3, 2025
@ShahzaibIbrahim ShahzaibIbrahim moved this from 🔖 Ready: Atomic to 🏗 In Work: Short in HiDPI Apr 3, 2025
@ShahzaibIbrahim
Copy link

ShahzaibIbrahim commented Apr 3, 2025

If we see the implementation of getFontData(FontData, zoom) in ScalingSWTFontRegistry

@Override
public Font getFont(FontData fontData, int zoom) {
	ScaledFontContainer container;
	if (customFontsKeyMap.containsKey(fontData)) {
		container = customFontsKeyMap.get(fontData);
	} else {
		container = new ScaledCustomFontContainer(fontData);
		customFontsKeyMap.put(fontData, container);
	}
	return container.getScaledFont(zoom);
}

If we change the font data it creates a new ScaledCustomFont which for me is correct behavior. Even later if the Font is requested for 200% zoom we will use the modified fontData and then scale it to 200% zoom.

Even if we use the clone image data to store it as a key. It won't be updated by reference but the output for your example will still remain.

found!
null

My conclusion is that we don't really need to do anything here.

@fedejeanne @akoch-yatta

@HeikoKlare
Copy link
Contributor

The issue is with the line adding to the map: customFontsKeyMap.put(fontData, container)

This results in the passed font data being placed inside the map, but the data be modified by the consumer that passed it in afterwards, i.e., something like:

registry.getFont(fontData, zoom);
fontData.height += 1;

@fedejeanne
Copy link
Author

My conclusion is that we don't really need to do anything here.

The resulting font might be fine but the fact that there is a dangling (unusable) entry in the map is the issue I described.

As Heiko mentioned, the issue described here is that when you change the internal state (a field) of the FontData that was used as a key, its hashCode and equals methods will return something different and therefore the call to if (customFontsKeyMap.containsKey(fontData)) will yield false even though the instance of FontData is still there.

If we make FontData immutable then the consumers will be forced to create a new instance every time they want to change its state, which would allow us to use a WeakHashMap and automatically remove the entry from the map once the (now unused) original FontData is nullified.

@HeikoKlare
Copy link
Contributor

If we make FontData immutable then the consumers will be forced to create a new instance every time they want to change its state, which would allow us to use a WeakHashMap and automatically remove the entry from the map once the (now unused) original FontData is nullified.

Just to avoid confusion and clarify that we are on the same page: the goal of this is not to make FontData immutable but just to clone the FontData stored inside the map, such that no one will be able to modify it.

@fedejeanne
Copy link
Author

Just to avoid confusion and clarify that we are on the same page: the goal of this is not to make FontData immutable but just to clone the FontData stored inside the map, such that no one will be able to modify it.

But that would mean that you will still have dangling entries in the map.

Try this:

public static void main(String[] args) {
	Map<FontData, String> map = new HashMap<>();

	FontData fontData = new FontData("f1", 10, 10);
	FontData clone = new FontData(fontData.getName(), fontData.getHeight(), fontData.getStyle());

	// store the clone but search for the original one
	map.put(clone, "found!");

	System.out.println(map.get(fontData));

	// modify the original one and search for it
	fontData.height += 1;
	System.out.println(map.get(fontData));
}

The output is still:

found!
null

Let's talk about it in the daily.

@fedejeanne
Copy link
Author

Let's talk about it in the daily.

Discussed: since the idea is to support the use-case where one creates "the same" font at a later point in time and passes it as a parameter to the getFont(FontData fd, ...) then storing a clone of the FontData in the map is the correct approach.

@ShahzaibIbrahim ShahzaibIbrahim moved this from 🏗 In Work: Short to 👀 In Review in HiDPI Apr 4, 2025
@github-project-automation github-project-automation bot moved this from 👀 In Review to ✅ Done in HiDPI Apr 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
HiDPI A HiDPI-Related Issue or Feature SWT Issue for SWT
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

3 participants