The following code crashes only on arm64 (not tested on armv7) without JIT:
let o = []; o[10000] = 10; o[20000] = 20; for (let k in o) delete o[k]
but not
let o = {};
o.a = 10;
o.b = 20;
for (let k in o)
delete o[k]
The difference is that in the first case, we are dealing with a SparseArray optimization.
The destructor of SparseArray shows that root() is nullable:
~SparseArray() {
if (root())
freeTree(header.left, Q_ALIGNOF(SparseArrayNode));
}
inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const
{
const SparseArrayNode *lb = root()->lowerBound(akey);
...
}
inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey)
{
SparseArrayNode *n = this;
SparseArrayNode *last = nullptr;
while (n) {
...
}
return last;
}
In debug mode, it probably will never crash because root()->lowerBound(akey) never dereferences this if this is null.
However, in release mode, the compiler knows that this is not nullable, so it can optimize the first `while( n )` check to `do {} while( n )` which it does on arm64 at least
| For Gerrit Dashboard: QTBUG-123596 | ||||||
|---|---|---|---|---|---|---|
| # | Subject | Branch | Project | Status | CR | V |
| 552654,2 | V4: Don't call methods on nullptr | dev | qt/qtdeclarative | Status: MERGED | +2 | 0 |
| 553151,2 | V4: Don't call methods on nullptr | 6.7 | qt/qtdeclarative | Status: MERGED | +2 | 0 |
| 553278,2 | V4: Don't call methods on nullptr | tqtc/lts-6.5 | qt/tqtc-qtdeclarative | Status: MERGED | +2 | 0 |
| 553445,2 | V4: Don't call methods on nullptr | tqtc/lts-6.2 | qt/tqtc-qtdeclarative | Status: MERGED | +2 | 0 |
| 553628,3 | V4: Don't call methods on nullptr | tqtc/lts-5.15 | qt/tqtc-qtdeclarative | Status: MERGED | +2 | 0 |