This change set incorporates a number of changes that all needed to
happen together:
* The imp is now the first field of the `objc_method` structure. This
makes it possible to extend the structure without breaking anything
that relies on being able to access the IMP.
* There is no owner in the slot, so we must use other mechanisms for
determining the owner of a method (e.g. whether the same method appears
in the superclass)
* Again, because there is no owner in the slot, we can't use this as a
fast path for finding the C++ construct / destruct methods. These are
now cached in the class structure when they are found.
* The version field is gone from the slot and now we provide a global
version. This is based on the observation that method replacements
are relatively infrequent and the overhead of invalidating all method
caches is cheaper than adding extra state for every (class, method)
pair.
* A number of the runtime functions are simplified because replacing
the IMP in a `Method` now implicitly updates the dtable.
In the legact ABI, superclass pointers are initially set to strings
containing the superclass name and the runtime fixes them up. In the
new ABI, the compiler sets up the linkage directly.
In the new ABI, we use the legacy type encoding in the selector and the
extended type encoding in the types field. We want to only expose the
legacy type encoding through existing APIs.
Extended type encodings add more detailed information to object and
block types:
* Objects are encoded as @"ClassName".
* Blocks are encoded as @?<parameter type signature>
In the new ABI, we use classic type encodings for selectors and extended
type encodings everywhere else.
With the old ABI, we generated a copy of every protocol in every
compilation unit that declared it. We now emit protocols only if they
are referenced (and have the linker deduplicate them).
This test was previously failing with the Apple runtime.
These are currently ignored, because they are only important when the
runtime has upgraded the protocol. The runtime will upgrade legacy
protocols, but they are not referenced by this indirection layer.
Without this fix, we would lose associated objects silently after adding
the 11th. We would also allocate full pages for each object after the
11th because we couldn't find empty slots.
* use fiber local storage if NO_PTHREADS is defined
* use critical sections instead of mutexes
Contributing-author: Ben Viglietta <benvi@microsoft.com>
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 =