| /** |
| This module contains utility functions to help the implementation of the runtime hook |
| |
| Copyright: Copyright Digital Mars 2000 - 2019. |
| License: Distributed under the |
| $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). |
| (See accompanying file LICENSE) |
| Source: $(DRUNTIMESRC core/internal/_array/_utils.d) |
| */ |
| module core.internal.array.utils; |
| |
| import core.internal.traits : Parameters; |
| import core.memory : GC; |
| |
| alias BlkAttr = GC.BlkAttr; |
| |
| auto gcStatsPure() nothrow pure |
| { |
| import core.memory : GC; |
| auto impureBypass = cast(GC.Stats function() pure nothrow)&GC.stats; |
| return impureBypass(); |
| } |
| |
| ulong accumulatePure(string file, int line, string funcname, string name, ulong size) nothrow pure |
| { |
| static ulong impureBypass(string file, int line, string funcname, string name, ulong size) @nogc nothrow |
| { |
| import core.internal.traits : externDFunc; |
| |
| alias accumulate = externDFunc!("rt.profilegc.accumulate", void function(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow); |
| accumulate(file, line, funcname, name, size); |
| return size; |
| } |
| |
| auto func = cast(ulong function(string file, int line, string funcname, string name, ulong size) @nogc nothrow pure)&impureBypass; |
| return func(file, line, funcname, name, size); |
| } |
| |
| version (D_ProfileGC) |
| { |
| /** |
| * TraceGC wrapper generator around the runtime hook `Hook`. |
| * Params: |
| * Type = The type of hook to report to accumulate |
| * Hook = The name hook to wrap |
| */ |
| template TraceHook(string Type, string Hook) |
| { |
| const char[] TraceHook = q{ |
| import core.internal.array.utils : gcStatsPure, accumulatePure; |
| |
| pragma(inline, false); |
| string name = } ~ "`" ~ Type ~ "`;" ~ q{ |
| |
| // FIXME: use rt.tracegc.accumulator when it is accessable in the future. |
| ulong currentlyAllocated = gcStatsPure().allocatedInCurrentThread; |
| |
| scope(exit) |
| { |
| ulong size = gcStatsPure().allocatedInCurrentThread - currentlyAllocated; |
| if (size > 0) |
| if (!accumulatePure(file, line, funcname, name, size)) { |
| // This 'if' and 'assert' is needed to force the compiler to not remove the call to |
| // `accumulatePure`. It really want to do that while optimizing as the function is |
| // `pure` and it does not influence the result of this hook. |
| |
| // `accumulatePure` returns the value of `size`, which can never be zero due to the |
| // previous 'if'. So this assert will never be triggered. |
| assert(0); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * TraceGC wrapper around runtime hook `Hook`. |
| * Params: |
| * T = Type of hook to report to accumulate |
| * Hook = The hook to wrap |
| * errorMessage = The error message incase `version != D_TypeInfo` |
| * file = File that called `_d_HookTraceImpl` |
| * line = Line inside of `file` that called `_d_HookTraceImpl` |
| * funcname = Function that called `_d_HookTraceImpl` |
| * parameters = Parameters that will be used to call `Hook` |
| * Bugs: |
| * This function template needs be between the compiler and a much older runtime hook that bypassed safety, |
| * purity, and throwabilty checks. To prevent breaking existing code, this function template |
| * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. |
| */ |
| auto _d_HookTraceImpl(T, alias Hook, string errorMessage)(Parameters!Hook parameters, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted pure |
| { |
| version (D_TypeInfo) |
| { |
| mixin(TraceHook!(T.stringof, __traits(identifier, Hook))); |
| return Hook(parameters); |
| } |
| else |
| assert(0, errorMessage); |
| } |
| } |
| |
| /** |
| * Check if the function `F` is calleable in a `nothrow` scope. |
| * Params: |
| * F = Function that does not take any parameters |
| * Returns: |
| * if the function is callable in a `nothrow` scope. |
| */ |
| enum isNoThrow(alias F) = is(typeof(() nothrow { F(); })); |
| |
| /** |
| * Check if the type `T`'s postblit is called in nothrow, if it exist |
| * Params: |
| * T = Type to check |
| * Returns: |
| * if the postblit is callable in a `nothrow` scope, if it exist. |
| * if it does not exist, return true. |
| */ |
| template isPostblitNoThrow(T) { |
| static if (__traits(isStaticArray, T)) |
| enum isPostblitNoThrow = isPostblitNoThrow!(typeof(T.init[0])); |
| else static if (__traits(hasMember, T, "__xpostblit") && |
| // Bugzilla 14746: Check that it's the exact member of S. |
| __traits(isSame, T, __traits(parent, T.init.__xpostblit))) |
| enum isPostblitNoThrow = isNoThrow!(T.init.__xpostblit); |
| else |
| enum isPostblitNoThrow = true; |
| } |
| |
| /** |
| * Allocate a memory block with appendable capabilities for array usage. |
| * |
| * Params: |
| * arrSize = size of the allocated array in bytes |
| * Returns: |
| * `void[]` matching requested size on success, `null` on failure. |
| */ |
| void[] __arrayAlloc(T)(size_t arrSize) @trusted |
| { |
| import core.lifetime : TypeInfoSize; |
| import core.internal.traits : hasIndirections; |
| |
| enum typeInfoSize = TypeInfoSize!T; |
| BlkAttr attr = BlkAttr.APPENDABLE; |
| |
| /* `extern(C++)` classes don't have a classinfo pointer in their vtable, |
| * so the GC can't finalize them. |
| */ |
| static if (typeInfoSize) |
| attr |= BlkAttr.FINALIZE; |
| static if (!hasIndirections!T) |
| attr |= BlkAttr.NO_SCAN; |
| |
| auto ptr = GC.malloc(arrSize, attr, typeid(T)); |
| if (ptr) |
| return ptr[0 .. arrSize]; |
| return null; |
| } |