-
-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathClassBuilder.cpp
More file actions
86 lines (76 loc) · 2.95 KB
/
Copy pathClassBuilder.cpp
File metadata and controls
86 lines (76 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "ClassBuilder.h"
#include <mutex>
#include "UnfairLock.h"
namespace tns {
namespace {
// objc_allocateClassPair only rejects names that are already *registered*, so
// two threads extending the same class name concurrently can both allocate it
// and register duplicate same-named classes (objc keeps both and name lookups
// become ambiguous). Serialize the whole allocate -> register window.
//
// This lock is a leaf — the section never acquires a v8::Locker — so it cannot
// form a cycle with the isolate locks.
UnfairMutex extendedClassRegistrationMutex;
// Registered objc class names are never reclaimed and the ladder below
// allocates suffixes densely under the lock, so a name's taken suffixes form a
// contiguous prefix. Gallop + binary-search with objc_getClass probes to find
// its end, so heavy same-name reuse (worker tests, HMR re-extends of one
// class) stays O(log N) per extend with no side state to grow.
int FirstFreeSuffix(const std::string& initialName) {
auto taken = [&initialName](int i) {
return objc_getClass((initialName + std::to_string(i)).c_str()) != nil;
};
int hi = 1;
while (taken(hi)) {
hi *= 2;
}
int lo = hi / 2;
while (lo + 1 < hi) {
int mid = lo + (hi - lo) / 2;
if (taken(mid)) {
lo = mid;
} else {
hi = mid;
}
}
return hi;
}
// Bounds only *consecutive* failures past the probed free position, i.e.
// allocation failing for reasons other than an ordinary name collision (which
// would otherwise loop forever holding the mutex).
constexpr int kMaxConsecutiveAllocFailures = 100;
} // namespace
// Moved this method in a separate .cpp file because ARC destroys the class
// created with objc_allocateClassPair when the control leaves this method scope
Class ClassBuilder::GetExtendedClass(const std::string& baseClassName,
const std::string& staticClassName,
int isolateId) {
Class baseClass = objc_getClass(baseClassName.c_str());
std::string name = staticClassName;
if (name.empty()) {
name = baseClassName;
name += std::to_string(isolateId);
name += "__";
name += std::to_string(++ClassBuilder::classNameCounter_);
}
// Allocation failure is the collision signal (objc_getClass beforehand
// would race), but that only detects *registered* names — hence the lock
// spanning allocate -> register.
std::lock_guard<UnfairMutex> lock(extendedClassRegistrationMutex);
Class clazz = objc_allocateClassPair(baseClass, name.c_str(), 0);
if (clazz == nil) {
std::string initialName = name;
int next = FirstFreeSuffix(initialName);
for (int attempts = 0;
clazz == nil && attempts < kMaxConsecutiveAllocFailures; attempts++) {
name = initialName + std::to_string(next++);
clazz = objc_allocateClassPair(baseClass, name.c_str(), 0);
}
if (clazz == nil) {
return nil;
}
}
objc_registerClassPair(clazz);
return clazz;
}
} // namespace tns