1/**
2 * Exception allocation, cloning, and release compiler support routines.
3 *
4 * Copyright: Copyright (c) 2017 by D Language Foundation
5 * License: Distributed under the
6 *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7 *    (See accompanying file LICENSE)
8 * Authors: Walter Bright
9 * Source: $(DRUNTIMESRC rt/_ehalloc.d)
10 */
11
12module rt.ehalloc;
13
14//debug = PRINTF;
15
16debug(PRINTF)
17{
18    import core.stdc.stdio;
19}
20
21/**********************************************
22 * Allocate an exception of type `ci` from the exception pool.
23 * It has the same interface as `rt.lifetime._d_newclass()`.
24 * The class type must be Throwable or derived from it,
25 * and cannot be a COM or C++ class. The compiler must enforce
26 * this.
27 * Returns:
28 *      default initialized instance of the type
29 */
30
31extern (C) Throwable _d_newThrowable(const TypeInfo_Class ci)
32{
33    debug(PRINTF) printf("_d_newThrowable(ci = %p, %s)\n", ci, cast(char *)ci.name);
34
35    assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass));
36    assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass));
37
38    import core.stdc.stdlib : malloc;
39    auto init = ci.initializer;
40    void* p = malloc(init.length);
41    if (!p)
42    {
43        import core.exception : onOutOfMemoryError;
44        onOutOfMemoryError();
45    }
46
47    debug(PRINTF) printf(" p = %p\n", p);
48
49    // initialize it
50    p[0 .. init.length] = init[];
51
52    if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers))
53    {
54        // Inform the GC about the pointers in the object instance
55        import core.memory : GC;
56
57        GC.addRange(p, init.length, ci);
58    }
59
60    debug(PRINTF) printf("initialization done\n");
61    Throwable t = cast(Throwable)p;
62    t.refcount() = 1;
63    return t;
64}
65
66
67/********************************************
68 * Delete exception instance `t` from the exception pool.
69 * Must have been allocated with `_d_newThrowable()`.
70 * This is meant to be called at the close of a catch block.
71 * It's nothrow because otherwise any function with a catch block could
72 * not be nothrow.
73 * Input:
74 *      t = Throwable
75 */
76
77nothrow extern (C) void _d_delThrowable(Throwable t)
78{
79    if (t)
80    {
81        debug(PRINTF) printf("_d_delThrowable(%p)\n", t);
82
83        /* If allocated by the GC, don't free it.
84         * Let the GC handle it.
85         * Supporting this is necessary while transitioning
86         * to this new scheme for allocating exceptions.
87         */
88        auto refcount = t.refcount();
89        if (refcount == 0)
90            return;     // it was allocated by the GC
91
92        if (refcount == 1)
93            assert(0);  // no zombie objects
94
95        t.refcount() = --refcount;
96        if (refcount > 1)
97            return;
98
99        TypeInfo_Class **pc = cast(TypeInfo_Class **)t;
100        if (*pc)
101        {
102            TypeInfo_Class ci = **pc;
103
104            if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers))
105            {
106                // Inform the GC about the pointers in the object instance
107                import core.memory : GC;
108                GC.removeRange(cast(void*) t);
109            }
110        }
111
112        try
113        {
114            import rt.lifetime : rt_finalize;
115            rt_finalize(cast(void*) t);
116        }
117        catch (Throwable t)
118        {
119            assert(0);  // should never happen since Throwable.~this() is nothrow
120        }
121        import core.stdc.stdlib : free;
122        debug(PRINTF) printf("free(%p)\n", t);
123        free(cast(void*) t);
124    }
125}
126