Skip to content

Commit e24865f

Browse files
committed
Improve memory manager for MSVC exceptions in c-tor args
MSVC calls allocator first before evaluating arguments for the actual object constructor. Since the evaluation phase can quite easily throw an error (like in `parse_list`), the memory manager is left in an undefined state. The object is already tracked but not initialized and we get an error if we try to `delete` the object (we should use free instead). Added macro to be used instead of overloaded new operator!
1 parent 8fbaaf7 commit e24865f

File tree

2 files changed

+29
-10
lines changed

2 files changed

+29
-10
lines changed

src/memory_manager.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace Sass {
1717
Memory_Manager<T>::~Memory_Manager()
1818
{
1919
// release memory for all controlled nodes
20-
// avoid calling erase for every single node
20+
// avoid calling erase for every single node
2121
for (size_t i = 0, S = nodes.size(); i < S; ++i) {
2222
deallocate(nodes[i]);
2323
}
@@ -26,11 +26,10 @@ namespace Sass {
2626
}
2727

2828
template <typename T>
29-
T* Memory_Manager<T>::operator()(T* np)
29+
T* Memory_Manager<T>::add(T* np)
3030
{
31-
// add to pool
32-
nodes.push_back(np);
33-
// return resource
31+
void* heap = (char*)np - 1;
32+
*((char*)((void*)heap)) = 1;
3433
return np;
3534
}
3635

@@ -44,13 +43,25 @@ namespace Sass {
4443
template <typename T>
4544
T* Memory_Manager<T>::allocate(size_t size)
4645
{
47-
return static_cast<T*>(operator new(size));
46+
// need additional memory for status header
47+
void* heap = malloc(sizeof(char) + size);
48+
// init internal status to zero
49+
*(static_cast<char*>(heap)) = 0;
50+
// the object lives on char further
51+
void* object = static_cast<char*>(heap) + 1;
52+
// add the memory under our management
53+
nodes.push_back(static_cast<T*>(object));
54+
// cast object to its final type
55+
return static_cast<T*>(object);
4856
}
4957

5058
template <typename T>
5159
void Memory_Manager<T>::deallocate(T* np)
5260
{
53-
delete np;
61+
void* object = static_cast<void*>(np);
62+
char* heap = static_cast<char*>(object) - 1;
63+
if (heap[0]) np->~T();
64+
free(heap);
5465
}
5566

5667
template <typename T>
@@ -67,7 +78,7 @@ namespace Sass {
6778
// remove from pool
6879
remove(np);
6980
// release memory
70-
delete np;
81+
deallocate(np);
7182
}
7283

7384
// compile implementation for AST_Node

src/memory_manager.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,25 @@ namespace Sass {
2424
void deallocate(T* np);
2525
void remove(T* np);
2626
void destroy(T* np);
27-
T* operator()(T* np);
27+
T* add(T* np);
2828

2929
};
3030
}
3131

3232
template <typename T>
3333
inline void* operator new(size_t size, Sass::Memory_Manager<T>& mem)
34-
{ return mem(mem.allocate(size)); }
34+
{ return mem.add(mem.allocate(size)); }
3535

3636
template <typename T>
3737
inline void operator delete(void *np, Sass::Memory_Manager<T>& mem)
3838
{ mem.destroy(reinterpret_cast<T*>(np)); }
3939

40+
///////////////////////////////////////////////////////////////////////////////
41+
// Use macros for the allocation task, since overloading operator `new`
42+
// has been proven to be flaky under certain compilers (see comment below).
43+
///////////////////////////////////////////////////////////////////////////////
44+
45+
#define SASS_MEMORY_NEW(mgr, Class, ...) \
46+
(static_cast<Class*>(mgr.add(new (mgr.allocate(sizeof(Class))) Class(__VA_ARGS__)))) \
47+
4048
#endif

0 commit comments

Comments
 (0)