Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit b984d53

Browse files
committed
[[ Bug 21992 ]] Fix memory leaks when sorting using MCSortnodes
This patch fixes memory leaks which can occur when using engine features which perform sorting using MCSortnodes. Specifically, when sorting fields, sorting cards of a stack, sorting the selection list for grouping and sorting the selection list for cloning. The leaks occur because of the presumption that MCSortnodes are POD types and thus require no specific construction or destruction. However, MCSortnodes fail to be POD as they have a destructor which releases any MCValueRef sort key which they hold. To fix the issue a new auto-class MCAutoArrayZeroedNonPod has been added to foundation-auto. This is direct replication of MCAutoArray but tailored to the case where the element type has a destructor and that destructor does nothing if the element is zeroed. This auto-class has then been used to replace previous uses of MCAutoArray in MCStack::sort, MCField::sort and MCSellst::sort. Additionally, the no longer used MCStringsExecSortOld function has been removed.
1 parent b5e0e6b commit b984d53

File tree

6 files changed

+116
-38
lines changed

6 files changed

+116
-38
lines changed

docs/notes/bugfix-21992.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix memory leaks when sorting cards, fields, or object selection

engine/src/exec-strings.cpp

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,41 +2252,6 @@ void MCStringsSortAddItem(MCExecContext &ctxt, MCSortnode *items, uint4 &nitems,
22522252
nitems++;
22532253
}
22542254

2255-
void MCStringsExecSortOld(MCExecContext& ctxt, Sort_type p_dir, Sort_type p_form, MCStringRef *p_strings_array, uindex_t p_count, MCExpression *p_by, MCStringRef*& r_sorted_array, uindex_t& r_sorted_count)
2256-
{
2257-
// OK-2008-12-11: [[Bug 7503]] - If there are 0 items in the string, don't carry out the search,
2258-
// this keeps the behavior consistent with previous versions of Revolution.
2259-
if (p_count < 1)
2260-
{
2261-
r_sorted_count = 0;
2262-
return;
2263-
}
2264-
2265-
// Now we know the item count, we can allocate an array of MCSortnodes to store them.
2266-
MCAutoArray<MCSortnode> t_items;
2267-
t_items.Extend(p_count + 1);
2268-
uindex_t t_added = 0;
2269-
2270-
// Next, populate the MCSortnodes with all the items to be sorted
2271-
for (uindex_t i = 0; i < p_count; i++)
2272-
{
2273-
MCStringsSortAddItem(ctxt, t_items . Ptr(), t_added, p_form, p_strings_array[i], p_by);
2274-
t_items[t_added - 1] . data = (void *)p_strings_array[i];
2275-
}
2276-
2277-
MCStringsSort(t_items . Ptr(), t_added, p_dir, p_form, ctxt . GetStringComparisonType());
2278-
2279-
MCAutoArray<MCStringRef> t_sorted;
2280-
2281-
for (uindex_t i = 0; i < t_added; i++)
2282-
{
2283-
t_sorted . Push((MCStringRef)t_items[i] . data);
2284-
MCValueRelease(t_items[i] . svalue);
2285-
}
2286-
2287-
t_sorted . Take(r_sorted_array, r_sorted_count);
2288-
}
2289-
22902255
////////////////////////////////////////////////////////////////////////////////
22912256

22922257
typedef bool (*comparator_t)(void *context, uindex_t left, uindex_t right);

engine/src/fields.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Exec_stat MCField::sort(MCExecContext &ctxt, uint4 parid, Chunk_term type,
8585
return ES_NORMAL;
8686
}
8787

88-
MCAutoArray<MCSortnode> items;
88+
MCAutoArrayZeroedNonPod<MCSortnode> items;
8989
uint4 nitems = 0;
9090
MCParagraph *pgptr;
9191

engine/src/sellst.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ void MCSellist::sort()
238238
Clean();
239239

240240
MCSelnode *optr = objects;
241-
MCAutoArray<MCSortnode> items;
241+
MCAutoArrayZeroedNonPod<MCSortnode> items;
242242
uint4 nitems = 0;
243243
MCCard *cptr = optr->m_ref->getcard();
244244
do

engine/src/stack3.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1903,7 +1903,7 @@ bool MCStack::sort(MCExecContext &ctxt, Sort_type dir, Sort_type form,
19031903
MCStack *olddefault = MCdefaultstackptr;
19041904
MCdefaultstackptr = this;
19051905
MCCard *cptr = curcard;
1906-
MCAutoArray<MCSortnode> items;
1906+
MCAutoArrayZeroedNonPod<MCSortnode> items;
19071907
uint4 nitems = 0;
19081908
MCerrorlock++;
19091909

libfoundation/include/foundation-auto.h

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,118 @@ template<typename T> class MCAutoArray
11371137
uindex_t m_size = 0;
11381138
};
11391139

1140+
/* Use the MCAutoArrayZeroedNonPod class when T has a destructor which will
1141+
* only delete non-null members. */
1142+
template<typename T> class MCAutoArrayZeroedNonPod
1143+
{
1144+
public:
1145+
constexpr MCAutoArrayZeroedNonPod() = default;
1146+
1147+
~MCAutoArrayZeroedNonPod(void)
1148+
{
1149+
for(uindex_t i = 0; i < m_size; i++)
1150+
m_ptr[i].~T();
1151+
MCMemoryDeleteArray(m_ptr);
1152+
}
1153+
1154+
//////////
1155+
1156+
T* Ptr()
1157+
{
1158+
return m_ptr;
1159+
}
1160+
1161+
uindex_t Size() const
1162+
{
1163+
return m_size;
1164+
}
1165+
1166+
//////////
1167+
1168+
bool New(uindex_t p_size)
1169+
{
1170+
MCAssert(m_ptr == nil);
1171+
return MCMemoryNewArray(p_size, m_ptr, m_size);
1172+
}
1173+
1174+
void Delete(void)
1175+
{
1176+
MCMemoryDeleteArray(m_ptr);
1177+
m_ptr = nil;
1178+
m_size = 0;
1179+
}
1180+
1181+
//////////
1182+
1183+
bool Resize(uindex_t p_new_size)
1184+
{
1185+
return MCMemoryResizeArray(p_new_size, m_ptr, m_size);
1186+
}
1187+
1188+
bool Extend(uindex_t p_new_size)
1189+
{
1190+
MCAssert(p_new_size >= m_size);
1191+
return Resize(p_new_size);
1192+
}
1193+
1194+
void Shrink(uindex_t p_new_size)
1195+
{
1196+
MCAssert(p_new_size <= m_size);
1197+
Resize(p_new_size);
1198+
}
1199+
1200+
//////////
1201+
1202+
bool Push(T p_value)
1203+
{
1204+
if (!Extend(m_size + 1))
1205+
return false;
1206+
m_ptr[m_size - 1] = p_value;
1207+
return true;
1208+
}
1209+
1210+
//////////
1211+
1212+
T*& PtrRef()
1213+
{
1214+
MCAssert(m_ptr == nil);
1215+
return m_ptr;
1216+
}
1217+
1218+
uindex_t& SizeRef()
1219+
{
1220+
MCAssert(m_size == 0);
1221+
return m_size;
1222+
}
1223+
1224+
//////////
1225+
1226+
void Take(T*& r_array, uindex_t& r_count)
1227+
{
1228+
r_array = m_ptr;
1229+
r_count = m_size;
1230+
1231+
m_ptr = nil;
1232+
m_size = 0;
1233+
}
1234+
1235+
//////////
1236+
1237+
T& operator [] (uindex_t p_index)
1238+
{
1239+
return m_ptr[p_index];
1240+
}
1241+
1242+
const T& operator [] (uindex_t p_index) const
1243+
{
1244+
return m_ptr[p_index];
1245+
}
1246+
1247+
private:
1248+
T *m_ptr = nullptr;
1249+
uindex_t m_size = 0;
1250+
};
1251+
11401252
// Version of MCAutoArray that applies the provided deallocator to each element of the array when freed
11411253
template <typename T, void (*FREE)(T)> class MCAutoCustomPointerArray
11421254
{

0 commit comments

Comments
 (0)