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

Vector2 division breaks the editor #7354

Closed
jeff-s-zhou opened this issue Dec 22, 2016 · 7 comments · Fixed by #7807 or #7815
Closed

Vector2 division breaks the editor #7354

jeff-s-zhou opened this issue Dec 22, 2016 · 7 comments · Fixed by #7807 or #7815

Comments

@jeff-s-zhou
Copy link

jeff-s-zhou commented Dec 22, 2016

Operating system or device - Godot version:
2.2.1

Issue description (what happened, and what was expected):
Typing out Vector2 division immediately crashes the editor, and opening the project afterwards causes the editor to immediately crash again until you remove the line using some other method.

Steps to reproduce:
I wrote this line in the _ready call of an otherwise empty script in an empty scene.

Vector2(5, 0)/Vector2(1, 0)

@RebelliousX
Copy link
Contributor

It is supposed to crash! In fact, I crashed trying to think about it, lol. There is no such thing as vector division. Vectors have sum, subtraction and 2 types of multiplication scalar (result is not a vector, just a number, a.k.a dot product) and cross product (result is a vector, which has many useful applications, determining if a vector is perpendicular to another is one of them).

This is not a bug, although, it should give a big fat error, not crashing the whole editor. Adding some exception for such expression would do it.

@jeff-s-zhou
Copy link
Author

I'm not sure I was clear here. I didn't RUN the script, and in fact the specific line was actually just an in between step as I was rewriting the code.

Just writing the line at all causes the editor to crash and to crash every time the project is opened.

@Zylann
Copy link
Contributor

Zylann commented Dec 31, 2016

Vector division is fine, it's just the same logic as member-wise operation as with add, multiply and subtract.
And as such, it also implies handling divide-by-zero cases.

I debugged the crash, and here is the call stack:

image

Weird thing that it tries to reload the script as if it was a tool. Of course, if it was, the editor itself would give errors too because the code is executed in the same context.

In gd_compiler.cpp:

			gdfunc->constants[idx]=*K;

idx was out of bounds, printed an error and returned null, causing a Variant assign error. Probably because evaluating the vector operation failed?

@Zylann
Copy link
Contributor

Zylann commented Dec 31, 2016

I found that if you have a constant that is NaN, the GDScript parser will silently put it as key of constants_map. However, NaN == NaN returns false, which probably makes dictionary lookup to fail all the times (because the key is compared for equality once found through the hash) and return null anyways. It probably messes up earlier because of this :s

		while((K=codegen.constant_map.next(K))) {
			idx = codegen.constant_map[*K];
			gdfunc->constants[idx]=*K;
		}

Iterating on the keys will work, however access will fail if K has NaN inside...

image

As a side note, access to the hash map was done with the non-const operator, so a new entry is created (because key comparing failed) but its value was left uninitialized to -1163005939, which is obviously out of bounds.

Several possible fixes, not exclusive:

  1. Prevent users from putting NaNs in hash-sensitive containers?
  2. Prevent NaN from appearing in this case by throwing an error if the compiled constant is wrong?
  3. Improve hashing so NaNs don't matter?

This particular case can be detected at parsing time by putting this in gd_parser.cpp in the _REDUCE_BINARY macro:

	if(res != res) {\
		printf("DDDDD Nan!!!! (3)\n");\
	}\

However it solves only one case and there are plenty of times a ConstantNode is created and assigned a value, and doesn't solves other cases where NaNs are used in hashes...

hpvb added a commit to hpvb/godot that referenced this issue Feb 14, 2017
After discussing this with Reduz this seemed like the best way to
fix godotengine#7354. This will make composite values that contain NaN in the same
places as well as the same other values compare as the same.

Additionally non-composite values now also compare equal if they are
both NaN. This breaks IEEE specifications but this is probably what most
users expect. There is a GDScript function check for NaN if the user
needs this information.

This fixes godotengine#7354 and probably also fixes godotengine#6947
@Zylann
Copy link
Contributor

Zylann commented Feb 14, 2017

Hmmm that fix isn't what I expected, and it makes comparisons slower (and I'm sure you can still inject NaNs :p)... but if @reduz thinks it's better... :)

@reduz
Copy link
Member

reduz commented Feb 14, 2017 via email

@akien-mga
Copy link
Member

Reopening as #7807 was reverted (better fix incoming, @hpvb and @tagcup are on it :D).

hpvb added a commit to hpvb/godot that referenced this issue Feb 16, 2017
This fixes HashMap where a key or part of a key is a floating point
number. To fix this the following has been done:

* HashMap now takes an extra template argument Comparator. This class
gets used to compare keys. The default Comperator now works correctly
for common types and floating point numbets.

* Variant implements ::hash_compare() now. This function implements
nan-safe comparison for all types with components that contain floating
point numbers.

* Variant now has a VariantComparator which uses Variant::hash_compare()
safely compare floating point components of variant's types.

* The hash functions for floating point numbers will now normalize NaN
values so that all floating point numbers that are NaN hash to the same
value.

C++ module writers that want to use HashMap internally in their modules
can now also safeguard against this crash by defining their on
Comperator class that safely compares their types.

GDScript users, or writers of modules that don't use HashMap internally
in their modules don't need to do anything.

This fixes godotengine#7354 and fixes godotengine#6947.
puchik added a commit to puchik/godot that referenced this issue Aug 31, 2023
Hash comparison for Variant continues to perform semantic/logical comparison with NaN's considered equal by default (to prevent godotengine#16114, godotengine#7354, godotengine#6947, godotengine#8081), but now optionally allows for numeric comparison that does not consider NaN's equal to support proper value comparison (for godotengine#72222)
mandryskowski pushed a commit to mandryskowski/godot that referenced this issue Oct 11, 2023
Hash comparison for Variant continues to perform semantic/logical comparison with NaN's considered equal by default (to prevent godotengine#16114, godotengine#7354, godotengine#6947, godotengine#8081), but now optionally allows for numeric comparison that does not consider NaN's equal to support proper value comparison (for godotengine#72222)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants