It looks like this was the initial intent of 4ea82e1, but as implemented
it would still only scan the unresolved class list once. Since
unresolved_class_list represents the head and classes are pushed onto
the head, any classes added to the resolution list after resolution
started would be skipped.
This commit fixes a data loss bug in our hopscotch table implementation.
Removing values from the table can result in other values becoming
disconnected and lost.
Let A, B, and C be values that all hash to cell 0.
Assume the hopscotch distance factor H = 2.
0 1 2
+-----+-----+-----+
| | | |
+-----+-----+-----+
After adding A
0 1 2
+-----+-----+-----+
| A | | |
+-----+-----+-----+
|
+-Neighbors =
After adding B
0 1 2
+-----+-----+-----+
| A | B | |
+-----+-----+-----+
|
+-Neighbors = 1
After adding C
0 1 2
+-----+-----+-----+
| A | B | C |
+-----+-----+-----+
|
+-Neighbors = 1, 2
If we then remove B,
0 1 2
+-----+-----+-----+
| A | [X] | C |
+-----+-----+-----+
|
+-Neighbors = 1, 2
* It is replaced with a placeholder [X].
* A's neighbor table is not updated to reflect the loss.
If we then remove A,
0 1 2
+-----+-----+-----+
| [X] | [X] | [C] |
+-----+-----+-----+
|
+-Neighbors = 2
* The table is rebalanced to promote A's lowest neighbor to the primary
cell position.
* C from cell 2 remains cell 0's neighbor.
The bug manifests if [X] the placeholder value passes the null check set
out in MAP_TABLE_VALUE_NULL; that is, the placeholder is "effectively
null".
Looking up the key that matches C will first evaluate its base cell, the
one that collided with the key in the first place. Since that is
placeholder [X], and [X] is "effectively null", the lookup stops.
C is never retrieved from the hash table.
---
The expedient solution to this bug is to update cell 0's neighbors when
B is first removed, effectively skipping the hole:
If we remove B as above,
0 1 2
+-----+-----+-----+
| A | [X] | C |
+-----+-----+-----+
|
+-Neighbors = 2 <<< HERE
but clear the neighbor bit for cell 1, the promotion that happens when A
is later removed places C in cell 0.
0 1 2
+-----+-----+-----+
| C | [X] | [X] |
+-----+-----+-----+
|
+-Neighbors =
macOS ships with libc++abi, which doesn't provide the hooks required
for interop, so this was causing all of the tests to fail to link in
the Travis macOS test. We're now correctly detecting that it won't
work on macOS and disabling those tests.
Throwing an Objective-C exception through a C++ catch block was broken.
This was because the C++ code inserts a cleanup handler to make sure
that it invokes `__cxa_end_catch`. Unwinding through this catchup
transformed the Objective-C exception into a C++ one. This case should
have been handled, except for two bugs:
1. A typo (`#ifdef` instead of `#ifndef`) meant that we were not
extracting the Objective-C exception from the C++ object.
2. We were skipping everything except catchalls after the search phase,
because we lose some information in the transformation.
Fixes#49
Weak refs were being left as dangling pointers after being deleted. The
load that caused the deallocation would return nil, but then the next
one would dereference a dangling pointer.
Fixes a bug introduced (replacing a similar bug) in the last rewrite of
objc_moveWeak. This version should now be correct.
objc_copyWeak was implemented in a naive way, which had a lot more
overhead than required.
Rather than tracking all of the locations of weak pointers, keep a weak
refcount. This should also make it easier to add finer-grained locking to weak
references.