mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
3.1 KiB
100 lines
3.1 KiB
/*
|
|
* There was an interesting bug up to Duktape 0.11.0, reported by Greg Burns.
|
|
*
|
|
* With certain object property add/delete sequences the object property entry
|
|
* part can grow without bound, even though the object only has a limited
|
|
* number of actually used properties.
|
|
*
|
|
* The root cause of this issue is the duk_hobject_props.c object entry part
|
|
* resize algorithm which computes the new entry part size based on the
|
|
* current allocated size (not *used size*). Suppose that the entry part
|
|
* for keys looks as follows ('.' is unused):
|
|
*
|
|
* [..........x]
|
|
*
|
|
* if the object entry part is resized, a larger one is allocated with keys
|
|
* compacted:
|
|
*
|
|
* [x...............]
|
|
*
|
|
* with a suitable insert/delete sequence (like the simple one below), you
|
|
* can then again reach the case:
|
|
*
|
|
* [...............x]
|
|
*
|
|
* ... and resize to:
|
|
*
|
|
* [x......................]
|
|
*
|
|
* and so on.
|
|
*
|
|
* The fix for this issue is to count the "used keys" explicitly before
|
|
* computing a new size for the object. The actual reallocation primitive
|
|
* will run through the keys anyway to compact (and possibly rehash) them,
|
|
* so this is not a major issue performance-wise.
|
|
*/
|
|
|
|
/*===
|
|
without compaction
|
|
entry count < 100: true
|
|
entry count < 100: true
|
|
with compaction
|
|
entry count < 100: true
|
|
entry count < 100: true
|
|
===*/
|
|
|
|
function objectEntryPartResizeTest(doCompact) {
|
|
var sparse = { 0: 0 };
|
|
var i;
|
|
var t, entryCount, entryNext;
|
|
|
|
for (i = 1; i < 10000; i++) {
|
|
sparse[i] = i;
|
|
delete sparse[i - 1];
|
|
|
|
if (doCompact) {
|
|
/* If the object is forcibly compacted, the undesired entry
|
|
* part behavior disappears even in Duktape 0.11.0 and prior.
|
|
*/
|
|
Duktape.compact(sparse);
|
|
}
|
|
}
|
|
|
|
/* Here the object entry part has been resized multiple times. We
|
|
* don't know the exact size, but since there is only one used key
|
|
* we can more or less safely assume the entry part should be smaller
|
|
* than 100 entries.
|
|
*
|
|
* NOTE: the "entry next" value is not the count of non-NULL key
|
|
* entries in the entry part. It's simply the next index to use
|
|
* when adding a new key, and is not necessarily 1 here.
|
|
*/
|
|
|
|
t = Duktape.info(sparse);
|
|
//print(t);
|
|
entryCount = t[5]; // version dependent, property entry allocated count
|
|
entryNext = t[6]; // version dependent, property entry next index
|
|
//print('entry next:', entryNext);
|
|
//print('entry count:', entryCount);
|
|
print('entry count < 100:', (entryCount < 100));
|
|
|
|
/* With the bug present, a compaction "fixes" the object. */
|
|
Duktape.compact(sparse);
|
|
|
|
t = Duktape.info(sparse);
|
|
//print(t);
|
|
entryCount = t[5]; // version dependent, property entry allocated count
|
|
entryNext = t[6]; // version dependent, property entry next index
|
|
//print('entry next:', entryNext);
|
|
//print('entry count:', entryCount);
|
|
print('entry count < 100:', (entryCount < 100));
|
|
}
|
|
|
|
try {
|
|
print('without compaction');
|
|
objectEntryPartResizeTest(false);
|
|
print('with compaction');
|
|
objectEntryPartResizeTest(true);
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
|