3211 lines
85 KiB
D
3211 lines
85 KiB
D
// **************************************************
|
|
// ** VALID FOR 1.42.0 (DMD v2.112.1, LLVM 21.1.8) **
|
|
// **************************************************
|
|
|
|
|
|
module object;
|
|
|
|
public import core.internal.cast_ : _d_cast;
|
|
|
|
alias AliasSeq(TList...) = TList;
|
|
|
|
// import core.arsd.memory_allocation;
|
|
|
|
import core.stdc.string;
|
|
import dlib.alloc;
|
|
import dlib.util;
|
|
|
|
import wasm;
|
|
|
|
export extern(C) void
|
|
__assert(const(char)* msg, const(char)* file, uint line)
|
|
{
|
|
|
|
Abortf("%s(%s): Assertion failure %s", Str(file), line, Str(msg));
|
|
}
|
|
|
|
version(CarelessAlocation)
|
|
{
|
|
version = inline_concat;
|
|
}
|
|
|
|
alias noreturn = typeof(*null);
|
|
|
|
alias hash_t = size_t; // For backwards compatibility only.
|
|
alias equals_t = bool; // For backwards compatibility only.
|
|
|
|
alias string = immutable(char)[];
|
|
alias wstring = immutable(wchar)[];
|
|
alias dstring = immutable(dchar)[];
|
|
|
|
alias size_t = typeof(int.sizeof);
|
|
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);
|
|
|
|
alias sizediff_t = ptrdiff_t;
|
|
|
|
enum immutable(void)* rtinfoNoPointers = null;
|
|
enum immutable(void)* rtinfoHasPointers = cast(void*)1;
|
|
|
|
int __cmp(C1, C2)(C1 lhs, C2 rhs)
|
|
if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) &&
|
|
(is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D"))))
|
|
{
|
|
static if (is(C1 == typeof(null)) && is(C2 == typeof(null)))
|
|
{
|
|
return 0;
|
|
}
|
|
else static if (is(C1 == typeof(null)))
|
|
{
|
|
// Regard null references as always being "less than"
|
|
return -1;
|
|
}
|
|
else static if (is(C2 == typeof(null)))
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (lhs is rhs)
|
|
return 0;
|
|
if (lhs is null)
|
|
return -1;
|
|
if (rhs is null)
|
|
return 1;
|
|
return lhs.opCmp(rhs);
|
|
}
|
|
}
|
|
|
|
class TypeInfo_AssociativeArray : TypeInfo
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString(value.toString(), "[", key.toString(), "]");
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_AssociativeArray)o;
|
|
return c && this.key == c.key &&
|
|
this.value == c.value;
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) @trusted const
|
|
{
|
|
return xopEquals(p1, p2);
|
|
}
|
|
|
|
override hash_t getHash(scope const void* p) nothrow @trusted const
|
|
{
|
|
return xtoHash(p);
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return (char[int]).sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. (char[int]).sizeof];
|
|
}
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
|
|
// private static import core.internal.newaa;
|
|
// alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
|
|
|
|
TypeInfo value;
|
|
TypeInfo key;
|
|
TypeInfo entry;
|
|
|
|
bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
|
|
hash_t function(scope const void*) nothrow @safe xtoHash;
|
|
|
|
alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
|
|
alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
|
|
|
|
override @property size_t talign() nothrow pure const
|
|
{
|
|
return (char[int]).alignof;
|
|
}
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
//arg1 = typeid(void*);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template RTInfoImpl(size_t[] pointerBitmap)
|
|
{
|
|
immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
|
|
}
|
|
|
|
template RTInfo(T)
|
|
{
|
|
enum pointerBitmap = __traits(getPointerBitmap, T);
|
|
static if (pointerBitmap[1 .. $] == size_t[pointerBitmap.length - 1].init)
|
|
enum RTInfo = rtinfoNoPointers;
|
|
else
|
|
enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
|
|
}
|
|
|
|
// then the entry point just for convenience so main works.
|
|
extern(C) int _Dmain(string[] args);
|
|
|
|
T[]
|
|
AllocTypeArray(T)(size_t count) @trusted nothrow @nogc
|
|
{
|
|
T* ptr = cast(T*)malloc(T.sizeof*count);
|
|
return ptr[0 .. count];
|
|
}
|
|
|
|
void*
|
|
AllocPtr(size_t size) @trusted nothrow @nogc
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
// basic array support {
|
|
|
|
template _arrayOp(Args...)
|
|
{
|
|
import core.internal.array.operations;
|
|
alias _arrayOp = arrayOp!Args;
|
|
}
|
|
|
|
extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz)
|
|
{
|
|
auto d = cast(ubyte*) dst;
|
|
auto s = cast(ubyte*) src;
|
|
auto len = dstlen * elemsz;
|
|
|
|
while(len) {
|
|
*d = *s;
|
|
d++;
|
|
s++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
void reserve(T)(ref T[] arr, size_t length) @trusted
|
|
{
|
|
arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0];
|
|
}
|
|
|
|
// TODO: fix these to use sprintf
|
|
|
|
extern(C) void _d_arraybounds(string file, size_t line)
|
|
{
|
|
Abortf("%s(%s): Range Error", file, line);
|
|
}
|
|
|
|
|
|
/// Called when an out of range slice of an array is created
|
|
extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length)
|
|
{
|
|
Abortf("%s(%s): Range error on slice creation %s is out of range on [%s .. %s]", file, line, length, lwr, upr);
|
|
}
|
|
|
|
/// Called when an out of range array index is accessed
|
|
extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
|
|
{
|
|
Abortf("%s(%s): Range error on indexing array %s is out of range of %s", file, line, index, length);
|
|
}
|
|
|
|
// }
|
|
|
|
extern(C) void _d_assert(string file, uint line) @trusted @nogc
|
|
{
|
|
Abortf("%s(%s): Assertion failure", file, line);
|
|
}
|
|
|
|
void _d_assertp(immutable(char)* file, uint line)
|
|
{
|
|
Abortf("%s(%s): Assertion failure", Str(file), line);
|
|
}
|
|
|
|
extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc
|
|
{
|
|
Abortf("%s(%s): Assertion failure - %s", file, line, msg);
|
|
}
|
|
|
|
void __switch_error(string file, size_t line) @trusted @nogc
|
|
{
|
|
_d_assert_msg("final switch error", file, line);
|
|
}
|
|
|
|
bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
|
|
{
|
|
if (lhs.length != rhs.length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach(i; 0..lhs.length)
|
|
{
|
|
if (lhs[i] != rhs[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// bare basics class support {
|
|
|
|
|
|
extern(C) Object _d_allocclass(TypeInfo_Class ti)
|
|
{
|
|
auto ptr = (cast(byte*)malloc(ti.m_init.length))[0 .. ti.m_init.length];
|
|
ptr[0 .. $] = ti.m_init[0 .. $];
|
|
return cast(Object) ptr.ptr;
|
|
}
|
|
|
|
extern(C) void* _d_dynamic_cast(Object o, TypeInfo_Class c) {
|
|
void* res = null;
|
|
size_t offset = 0;
|
|
if (o && _d_isbaseof2(typeid(o), c, offset))
|
|
{
|
|
res = cast(void*) o + offset;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe
|
|
{
|
|
import core.internal.cast_ : areClassInfosEqual;
|
|
|
|
if (areClassInfosEqual(oc, c))
|
|
return true;
|
|
|
|
do
|
|
{
|
|
if (oc.base && areClassInfosEqual(oc.base, c))
|
|
return true;
|
|
|
|
// Bugzilla 2013: Use depth-first search to calculate offset
|
|
// from the derived (oc) to the base (c).
|
|
foreach (iface; oc.interfaces)
|
|
{
|
|
if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
|
|
return true;
|
|
}
|
|
|
|
oc = oc.base;
|
|
} while (oc);
|
|
|
|
return false;
|
|
}
|
|
|
|
/*************************************
|
|
* Attempts to cast Object o to class c.
|
|
* Returns o if successful, null if not.
|
|
*/
|
|
extern(C) void* _d_interface_cast(void* p, TypeInfo_Class c)
|
|
{
|
|
if (!p)
|
|
return null;
|
|
|
|
Interface* pi = **cast(Interface***) p;
|
|
return _d_dynamic_cast(cast(Object)(p - pi.offset), c);
|
|
}
|
|
|
|
|
|
extern(C)
|
|
int _d_isbaseof2(scope TypeInfo_Class oc, scope const TypeInfo_Class c, scope ref size_t offset) @safe
|
|
|
|
{
|
|
if (oc is c)
|
|
return true;
|
|
|
|
do
|
|
{
|
|
if (oc.base is c)
|
|
return true;
|
|
|
|
// Bugzilla 2013: Use depth-first search to calculate offset
|
|
// from the derived (oc) to the base (c).
|
|
foreach (iface; oc.interfaces)
|
|
{
|
|
if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
|
|
{
|
|
offset += iface.offset;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
oc = oc.base;
|
|
} while (oc);
|
|
|
|
return false;
|
|
}
|
|
|
|
int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted pure @nogc nothrow
|
|
if (__traits(isScalar, T))
|
|
{
|
|
// Compute U as the implementation type for T
|
|
static if (is(T == ubyte) || is(T == void) || is(T == bool))
|
|
alias U = char;
|
|
else static if (is(T == wchar))
|
|
alias U = ushort;
|
|
else static if (is(T == dchar))
|
|
alias U = uint;
|
|
else static if (is(T == ifloat))
|
|
alias U = float;
|
|
else static if (is(T == idouble))
|
|
alias U = double;
|
|
else static if (is(T == ireal))
|
|
alias U = real;
|
|
else
|
|
alias U = T;
|
|
|
|
static if (is(U == char))
|
|
{
|
|
int dstrcmp(scope const char[] s1, scope const char[] s2 ) @trusted pure @nogc nothrow
|
|
{
|
|
immutable len = s1.length <= s2.length ? s1.length : s2.length;
|
|
if (__ctfe)
|
|
{
|
|
foreach (const u; 0 .. len)
|
|
{
|
|
if (s1[u] != s2[u])
|
|
return s1[u] > s2[u] ? 1 : -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const ret = memcmp( s1.ptr, s2.ptr, len );
|
|
if ( ret )
|
|
return ret;
|
|
}
|
|
return (s1.length > s2.length) - (s1.length < s2.length);
|
|
}
|
|
|
|
return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
|
|
}
|
|
else static if (!is(U == T))
|
|
{
|
|
// Reuse another implementation
|
|
return __cmp(cast(U[]) lhs, cast(U[]) rhs);
|
|
}
|
|
else
|
|
{
|
|
version (BigEndian)
|
|
static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P))
|
|
{
|
|
if (!__ctfe)
|
|
{
|
|
import core.stdc.string : memcmp;
|
|
int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof);
|
|
if (c)
|
|
return c;
|
|
static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2)
|
|
return cast(int) lhs.length - cast(int) rhs.length;
|
|
else
|
|
return int(lhs.length > rhs.length) - int(lhs.length < rhs.length);
|
|
}
|
|
}
|
|
|
|
immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
|
|
foreach (const u; 0 .. len)
|
|
{
|
|
auto a = lhs.ptr[u], b = rhs.ptr[u];
|
|
static if (is(T : creal))
|
|
{
|
|
// Use rt.cmath2._Ccmp instead ?
|
|
// Also: if NaN is present, numbers will appear equal.
|
|
auto r = (a.re > b.re) - (a.re < b.re);
|
|
if (!r) r = (a.im > b.im) - (a.im < b.im);
|
|
}
|
|
else
|
|
{
|
|
// This pattern for three-way comparison is better than conditional operators
|
|
// See e.g. https://godbolt.org/z/3j4vh1
|
|
const r = (a > b) - (a < b);
|
|
}
|
|
if (r) return r;
|
|
}
|
|
return (lhs.length > rhs.length) - (lhs.length < rhs.length);
|
|
}
|
|
}
|
|
|
|
template Unqual(T : const U, U)
|
|
{
|
|
static if (is(U == shared V, V))
|
|
alias Unqual = V;
|
|
else
|
|
alias Unqual = U;
|
|
}
|
|
|
|
// This function is called by the compiler when dealing with array
|
|
// comparisons in the semantic analysis phase of CmpExp. The ordering
|
|
// comparison is lowered to a call to this template.
|
|
int __cmp(T1, T2)(T1[] s1, T2[] s2)
|
|
if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
|
|
{
|
|
alias U1 = Unqual!T1;
|
|
alias U2 = Unqual!T2;
|
|
|
|
static if (is(U1 == void) && is(U2 == void))
|
|
static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
|
|
else
|
|
static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
|
|
|
|
// All unsigned byte-wide types = > dstrcmp
|
|
immutable len = s1.length <= s2.length ? s1.length : s2.length;
|
|
|
|
foreach (const u; 0 .. len)
|
|
{
|
|
static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
|
|
{
|
|
auto c = __cmp(at(s1, u), at(s2, u));
|
|
if (c != 0)
|
|
return c;
|
|
}
|
|
else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
|
|
{
|
|
auto c = at(s1, u).opCmp(at(s2, u));
|
|
if (c != 0)
|
|
return c;
|
|
}
|
|
else static if (__traits(compiles, at(s1, u) < at(s2, u)))
|
|
{
|
|
if (int result = (at(s1, u) > at(s2, u)) - (at(s1, u) < at(s2, u)))
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
// TODO: fix this legacy bad behavior, see
|
|
// https://issues.dlang.org/show_bug.cgi?id=17244
|
|
static assert(is(U1 == U2), "Internal error.");
|
|
import core.stdc.string : memcmp;
|
|
auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
|
|
if (c != 0)
|
|
return c;
|
|
}
|
|
}
|
|
return (s1.length > s2.length) - (s1.length < s2.length);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Support for switch statements switching on strings.
|
|
Params:
|
|
caseLabels = sorted array of strings generated by compiler. Note the
|
|
strings are sorted by length first, and then lexicographically.
|
|
condition = string to look up in table
|
|
Returns:
|
|
index of match in caseLabels, a negative integer if not found
|
|
*/
|
|
int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc
|
|
{
|
|
// This closes recursion for other cases.
|
|
static if (caseLabels.length == 0)
|
|
{
|
|
return int.min;
|
|
}
|
|
else static if (caseLabels.length == 1)
|
|
{
|
|
return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
|
|
}
|
|
// To be adjusted after measurements
|
|
// Compile-time inlined binary search.
|
|
else static if (caseLabels.length < 7)
|
|
{
|
|
int r = void;
|
|
enum mid = cast(int)caseLabels.length / 2;
|
|
if (condition.length == caseLabels[mid].length)
|
|
{
|
|
r = __cmp(condition, caseLabels[mid]);
|
|
if (r == 0) return mid;
|
|
}
|
|
else
|
|
{
|
|
// Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1
|
|
r = ((condition.length > caseLabels[mid].length) << 1) - 1;
|
|
}
|
|
|
|
if (r < 0)
|
|
{
|
|
// Search the left side
|
|
return __switch!(T, caseLabels[0 .. mid])(condition);
|
|
}
|
|
else
|
|
{
|
|
// Search the right side
|
|
return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Need immutable array to be accessible in pure code, but case labels are
|
|
// currently coerced to the switch condition type (e.g. const(char)[]).
|
|
pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items)
|
|
{
|
|
assert(__ctfe); // only @safe for CTFE
|
|
immutable T[][caseLabels.length] result = cast(immutable)(items[]);
|
|
return result;
|
|
}
|
|
static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]);
|
|
|
|
// Run-time binary search in a static array of labels.
|
|
return __switchSearch!T(cases[], condition);
|
|
}
|
|
}
|
|
|
|
// binary search in sorted string cases, also see `__switch`.
|
|
private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc
|
|
{
|
|
size_t low = 0;
|
|
size_t high = cases.length;
|
|
|
|
do
|
|
{
|
|
auto mid = (low + high) / 2;
|
|
int r = void;
|
|
if (condition.length == cases[mid].length)
|
|
{
|
|
r = __cmp(condition, cases[mid]);
|
|
if (r == 0) return cast(int) mid;
|
|
}
|
|
else
|
|
{
|
|
// Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc
|
|
r = ((condition.length > cases[mid].length) << 1) - 1;
|
|
}
|
|
|
|
if (r > 0) low = mid + 1;
|
|
else high = mid;
|
|
}
|
|
while (low < high);
|
|
|
|
// Not found
|
|
return -1;
|
|
}
|
|
|
|
//TODO: Support someday?
|
|
extern(C) void _d_throw_exception(Throwable o)
|
|
{
|
|
assert(false, "Exception throw");
|
|
}
|
|
|
|
|
|
// for closures
|
|
extern(C) void* _d_allocmemory(size_t sz)
|
|
{
|
|
return malloc(sz);
|
|
}
|
|
|
|
///For POD structures
|
|
extern (C) void* _d_allocmemoryT(TypeInfo ti)
|
|
{
|
|
return malloc(ti.tsize);
|
|
}
|
|
|
|
|
|
class Object
|
|
{
|
|
/// Convert Object to human readable string
|
|
string toString() { return "Object"; }
|
|
/// Compute hash function for Object
|
|
size_t toHash() @trusted nothrow
|
|
{
|
|
auto addr = cast(size_t)cast(void*)this;
|
|
return addr ^ (addr >>> 4);
|
|
}
|
|
|
|
/// Compare against another object. NOT IMPLEMENTED!
|
|
int opCmp(Object o) { assert(false, "not implemented"); }
|
|
/// Check equivalence againt another object
|
|
bool opEquals(Object o) { return this is o; }
|
|
}
|
|
|
|
/// Compare to objects
|
|
bool opEquals(Object lhs, Object rhs)
|
|
{
|
|
// If aliased to the same object or both null => equal
|
|
if (lhs is rhs) return true;
|
|
|
|
// If either is null => non-equal
|
|
if (lhs is null || rhs is null) return false;
|
|
|
|
if (!lhs.opEquals(rhs)) return false;
|
|
|
|
// If same exact type => one call to method opEquals
|
|
if (typeid(lhs) is typeid(rhs) ||
|
|
!__ctfe && typeid(lhs).opEquals(typeid(rhs)))
|
|
/* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't
|
|
(issue 7147). But CTFE also guarantees that equal TypeInfos are
|
|
always identical. So, no opEquals needed during CTFE. */
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// General case => symmetric calls to method opEquals
|
|
return rhs.opEquals(lhs);
|
|
}
|
|
/************************
|
|
* Returns true if lhs and rhs are equal.
|
|
*/
|
|
bool opEquals(const Object lhs, const Object rhs)
|
|
{
|
|
// A hack for the moment.
|
|
return opEquals(cast()lhs, cast()rhs);
|
|
}
|
|
|
|
class TypeInfo
|
|
{
|
|
override string toString() const @safe nothrow
|
|
{
|
|
return typeid(this).name;
|
|
}
|
|
|
|
override size_t toHash() @trusted const nothrow
|
|
{
|
|
return hashOf(this.toString());
|
|
}
|
|
|
|
override int opCmp(Object rhs)
|
|
{
|
|
if (this is rhs)
|
|
return 0;
|
|
auto ti = cast(TypeInfo) rhs;
|
|
if (ti is null)
|
|
return 1;
|
|
return __cmp(this.toString(), ti.toString());
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
return opEquals(cast(TypeInfo) o);
|
|
}
|
|
|
|
bool opEquals(const TypeInfo ti) @safe nothrow const
|
|
{
|
|
/* TypeInfo instances are singletons, but duplicates can exist
|
|
* across DLL's. Therefore, comparing for a name match is
|
|
* sufficient.
|
|
*/
|
|
if (this is ti)
|
|
return true;
|
|
return ti && this.toString() == ti.toString();
|
|
}
|
|
|
|
/**
|
|
* Computes a hash of the instance of a type.
|
|
* Params:
|
|
* p = pointer to start of instance of the type
|
|
* Returns:
|
|
* the hash
|
|
* Bugs:
|
|
* fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface.
|
|
*/
|
|
size_t getHash(scope const void* p) @trusted nothrow const
|
|
{
|
|
// by default, do not assume anything about the type
|
|
return 0;
|
|
}
|
|
|
|
/// Compares two instances for equality.
|
|
bool equals(in void* p1, in void* p2) const { return p1 == p2; }
|
|
|
|
/// Compares two instances for <, ==, or >.
|
|
int compare(in void* p1, in void* p2) const { return _xopCmp(p1, p2); }
|
|
|
|
/// Returns size of the type.
|
|
@property size_t tsize() nothrow pure const @safe @nogc { return 0; }
|
|
|
|
/// Swaps two instances of the type.
|
|
void swap(void* p1, void* p2) const
|
|
{
|
|
size_t remaining = tsize;
|
|
// If the type might contain pointers perform the swap in pointer-sized
|
|
// chunks in case a garbage collection pass interrupts this function.
|
|
if ((cast(size_t) p1 | cast(size_t) p2) % (void*).alignof == 0)
|
|
{
|
|
while (remaining >= (void*).sizeof)
|
|
{
|
|
void* tmp = *cast(void**) p1;
|
|
*cast(void**) p1 = *cast(void**) p2;
|
|
*cast(void**) p2 = tmp;
|
|
p1 += (void*).sizeof;
|
|
p2 += (void*).sizeof;
|
|
remaining -= (void*).sizeof;
|
|
}
|
|
}
|
|
for (size_t i = 0; i < remaining; i++)
|
|
{
|
|
byte t = (cast(byte *)p1)[i];
|
|
(cast(byte*)p1)[i] = (cast(byte*)p2)[i];
|
|
(cast(byte*)p2)[i] = t;
|
|
}
|
|
}
|
|
|
|
/** Get TypeInfo for 'next' type, as defined by what kind of type this is,
|
|
null if none. */
|
|
@property inout(TypeInfo) next() nothrow pure inout @nogc { return null; }
|
|
|
|
/**
|
|
* Return default initializer. If the type should be initialized to all
|
|
* zeros, an array with a null ptr and a length equal to the type size will
|
|
* be returned. For static arrays, this returns the default initializer for
|
|
* a single element of the array, use `tsize` to get the correct size.
|
|
*/
|
|
version (LDC)
|
|
{
|
|
// LDC uses TypeInfo's vtable for the typeof(null) type:
|
|
// %"typeid(typeof(null))" = type { %object.TypeInfo.__vtbl*, i8* }
|
|
// Therefore this class cannot be abstract, and all methods need implementations.
|
|
// Tested by test14754() in runnable/inline.d, and a unittest below.
|
|
const(void)[] initializer() nothrow pure const @trusted @nogc
|
|
{
|
|
return (cast(const(void)*) null)[0 .. typeof(null).sizeof];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
abstract const(void)[] initializer() nothrow pure const @safe @nogc;
|
|
}
|
|
|
|
/** Get flags for type: 1 means GC should scan for pointers,
|
|
2 means arg of this type is passed in SIMD register(s) if available */
|
|
@property uint flags() nothrow pure const @safe @nogc { return 0; }
|
|
|
|
/// Get type information on the contents of the type; null if not available
|
|
const(OffsetTypeInfo)[] offTi() const { return null; }
|
|
/// Run the destructor on the object and all its sub-objects
|
|
void destroy(void* p) const {}
|
|
/// Run the postblit on the object and all its sub-objects
|
|
void postblit(void* p) const {}
|
|
|
|
|
|
/// Return alignment of type
|
|
@property size_t talign() nothrow pure const @safe @nogc { return tsize; }
|
|
|
|
/** Return internal info on arguments fitting into 8byte.
|
|
* See X86-64 ABI 3.2.3
|
|
*/
|
|
version (WithArgTypes) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow
|
|
{
|
|
arg1 = this;
|
|
return 0;
|
|
}
|
|
|
|
/** Return info used by the garbage collector to do precise collection.
|
|
*/
|
|
@property immutable(void)* rtInfo() nothrow pure const @trusted @nogc { return rtinfoHasPointers; } // better safe than sorry
|
|
}
|
|
|
|
class TypeInfo_Class : TypeInfo
|
|
{
|
|
override string toString() const pure { return name; }
|
|
|
|
override bool opEquals(const TypeInfo o) const
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Class)o;
|
|
return c && this.name == c.name;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
auto o = *cast(Object*)p;
|
|
return o ? o.toHash() : 0;
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
Object o1 = *cast(Object*)p1;
|
|
Object o2 = *cast(Object*)p2;
|
|
|
|
return (o1 is o2) || (o1 && o1.opEquals(o2));
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
Object o1 = *cast(Object*)p1;
|
|
Object o2 = *cast(Object*)p2;
|
|
int c = 0;
|
|
|
|
// Regard null references as always being "less than"
|
|
if (o1 !is o2)
|
|
{
|
|
if (o1)
|
|
{
|
|
if (!o2)
|
|
c = 1;
|
|
else
|
|
c = o1.opCmp(o2);
|
|
}
|
|
else
|
|
c = -1;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return Object.sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() nothrow pure const @safe
|
|
{
|
|
return m_init;
|
|
}
|
|
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
override @property const(OffsetTypeInfo)[] offTi() nothrow pure const
|
|
{
|
|
return m_offTi;
|
|
}
|
|
|
|
final @property auto info() @safe @nogc nothrow pure const return { return this; }
|
|
final @property auto typeinfo() @safe @nogc nothrow pure const return { return this; }
|
|
|
|
byte[] m_init; /** class static initializer
|
|
* (init.length gives size in bytes of class)
|
|
*/
|
|
string name; /// class name
|
|
void*[] vtbl; /// virtual function pointer table
|
|
Interface[] interfaces; /// interfaces this class implements
|
|
TypeInfo_Class base; /// base class
|
|
void* destructor;
|
|
void function(Object) classInvariant;
|
|
enum ClassFlags : ushort
|
|
{
|
|
isCOMclass = 0x1,
|
|
noPointers = 0x2,
|
|
hasOffTi = 0x4,
|
|
hasCtor = 0x8,
|
|
hasGetMembers = 0x10,
|
|
hasTypeInfo = 0x20,
|
|
isAbstract = 0x40,
|
|
isCPPclass = 0x80,
|
|
hasDtor = 0x100,
|
|
hasNameSig = 0x200,
|
|
}
|
|
ClassFlags m_flags;
|
|
ushort depth; /// inheritance distance from Object
|
|
void* deallocator;
|
|
OffsetTypeInfo[] m_offTi;
|
|
void function(Object) defaultConstructor; // default Constructor
|
|
|
|
immutable(void)* m_RTInfo; // data for precise GC
|
|
override @property immutable(void)* rtInfo() const { return m_RTInfo; }
|
|
|
|
uint[4] nameSig; /// unique signature for `name`
|
|
|
|
/**
|
|
* Search all modules for TypeInfo_Class corresponding to classname.
|
|
* Returns: null if not found
|
|
*/
|
|
static const(TypeInfo_Class) find(const scope char[] classname)
|
|
{
|
|
foreach (m; ModuleInfo)
|
|
{
|
|
if (m)
|
|
{
|
|
//writefln("module %s, %d", m.name, m.localClasses.length);
|
|
foreach (c; m.localClasses)
|
|
{
|
|
if (c is null)
|
|
continue;
|
|
//writefln("\tclass %s", c.name);
|
|
if (c.name == classname)
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create instance of Object represented by 'this'.
|
|
*/
|
|
Object create() const
|
|
{
|
|
if (m_flags & 8 && !defaultConstructor)
|
|
return null;
|
|
if (m_flags & 64) // abstract
|
|
return null;
|
|
Object o = _d_newclass(this);
|
|
if (m_flags & 8 && defaultConstructor)
|
|
{
|
|
defaultConstructor(o);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the class described by `child` derives from or is
|
|
* the class described by this `TypeInfo_Class`. Always returns false
|
|
* if the argument is null.
|
|
*
|
|
* Params:
|
|
* child = TypeInfo for some class
|
|
* Returns:
|
|
* true if the class described by `child` derives from or is the
|
|
* class described by this `TypeInfo_Class`.
|
|
*/
|
|
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
|
|
{
|
|
if (m_init.length)
|
|
{
|
|
// If this TypeInfo_Class represents an actual class we only need
|
|
// to check the child and its direct ancestors.
|
|
for (auto ti = cast() child; ti !is null; ti = ti.base)
|
|
if (ti is this)
|
|
return true;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// If this TypeInfo_Class is the .info field of a TypeInfo_Interface
|
|
// we also need to recursively check the child's interfaces.
|
|
return child !is null && _d_isbaseof(cast() child, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
alias ClassInfo = TypeInfo_Class;
|
|
|
|
|
|
extern (C) Object _d_newclass(const ClassInfo ci) nothrow
|
|
{
|
|
const initializer = ci.initializer;
|
|
|
|
auto p = AllocPtr(initializer.length);
|
|
memcpy(p, initializer.ptr, initializer.length);
|
|
return cast(Object) p;
|
|
}
|
|
|
|
void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
|
|
{
|
|
import core.internal.destruction : destructRecurse;
|
|
|
|
destructRecurse(obj);
|
|
|
|
static if (initialize)
|
|
{
|
|
import core.internal.lifetime : emplaceInitializer;
|
|
emplaceInitializer(obj); // emplace T.init
|
|
}
|
|
}
|
|
|
|
private extern (D) nothrow alias void function (Object) fp_t;
|
|
private extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow
|
|
{
|
|
auto ppv = cast(void**) p;
|
|
if (!p || !*ppv)
|
|
return;
|
|
|
|
auto pc = cast(TypeInfo_Class*) *ppv;
|
|
if (det)
|
|
{
|
|
auto c = *pc;
|
|
do
|
|
{
|
|
if (c.destructor)
|
|
(cast(fp_t) c.destructor)(cast(Object) p); // call destructor
|
|
}
|
|
while ((c = c.base) !is null);
|
|
}
|
|
|
|
if (resetMemory)
|
|
{
|
|
auto w = (*pc).initializer;
|
|
p[0 .. w.length] = w[];
|
|
}
|
|
*ppv = null; // zero vptr even if `resetMemory` is false
|
|
}
|
|
extern(C) void _d_callfinalizer(void* p)
|
|
{
|
|
rt_finalize2(p);
|
|
}
|
|
|
|
void destroy(bool initialize = true, T)(T obj) if (is(T == class))
|
|
{
|
|
static if (__traits(getLinkage, T) == "C++")
|
|
{
|
|
static if (__traits(hasMember, T, "__xdtor"))
|
|
obj.__xdtor();
|
|
|
|
static if (initialize)
|
|
{
|
|
const initializer = __traits(initSymbol, T);
|
|
(cast(void*)obj)[0 .. initializer.length] = initializer[];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bypass overloaded opCast
|
|
auto ptr = (() @trusted => *cast(void**) &obj)();
|
|
rt_finalize2(ptr, true, initialize);
|
|
}
|
|
}
|
|
void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
|
|
{
|
|
static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
|
|
|
|
destroy!initialize(cast(Object)obj);
|
|
}
|
|
void destroy(bool initialize = true, T)(ref T obj)
|
|
if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
|
|
{
|
|
static if (initialize)
|
|
obj = T.init;
|
|
}
|
|
|
|
|
|
class TypeInfo_Pointer : TypeInfo
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString(m_next.toString(), "*");
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Pointer)o;
|
|
return c && this.m_next == c.m_next;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
size_t addr = cast(size_t) *cast(const void**)p;
|
|
return addr ^ (addr >> 4);
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
return *cast(void**)p1 == *cast(void**)p2;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
const v1 = *cast(void**) p1, v2 = *cast(void**) p2;
|
|
return (v1 > v2) - (v1 < v2);
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return (void*).sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. (void*).sizeof];
|
|
}
|
|
|
|
override void swap(void* p1, void* p2) const
|
|
{
|
|
void* tmp = *cast(void**)p1;
|
|
*cast(void**)p1 = *cast(void**)p2;
|
|
*cast(void**)p2 = tmp;
|
|
}
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return m_next; }
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
TypeInfo m_next;
|
|
}
|
|
|
|
class TypeInfo_Array : TypeInfo
|
|
{
|
|
override string toString() const { return MakeString(value.toString(), "[]"); }
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Array)o;
|
|
return c && this.value == c.value;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
void[] a = *cast(void[]*)p;
|
|
return getArrayHash(value, a.ptr, a.length);
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
void[] a1 = *cast(void[]*)p1;
|
|
void[] a2 = *cast(void[]*)p2;
|
|
if (a1.length != a2.length)
|
|
return false;
|
|
size_t sz = value.tsize;
|
|
for (size_t i = 0; i < a1.length; i++)
|
|
{
|
|
if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
void[] a1 = *cast(void[]*)p1;
|
|
void[] a2 = *cast(void[]*)p2;
|
|
size_t sz = value.tsize;
|
|
size_t len = a1.length;
|
|
|
|
if (a2.length < len)
|
|
len = a2.length;
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz);
|
|
if (result)
|
|
return result;
|
|
}
|
|
return (a1.length > a2.length) - (a1.length < a2.length);
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return (void[]).sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. (void[]).sizeof];
|
|
}
|
|
|
|
override void swap(void* p1, void* p2) const
|
|
{
|
|
void[] tmp = *cast(void[]*)p1;
|
|
*cast(void[]*)p1 = *cast(void[]*)p2;
|
|
*cast(void[]*)p2 = tmp;
|
|
}
|
|
|
|
TypeInfo value;
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout
|
|
{
|
|
return value;
|
|
}
|
|
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
override @property size_t talign() nothrow pure const
|
|
{
|
|
return (void[]).alignof;
|
|
}
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(size_t);
|
|
arg2 = typeid(void*);
|
|
return 0;
|
|
}
|
|
|
|
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(void[]); }
|
|
}
|
|
|
|
string
|
|
MakeString(Args...)(Args args) nothrow @trusted
|
|
{
|
|
uint length;
|
|
|
|
static foreach(i; 0 .. Args.length)
|
|
{
|
|
static assert(is(Args[i] == char[]) || is(Args[i] == const(char)[]) || is(Args[i] == string), "Only string parameters are supported for MakeString");
|
|
|
|
length += args[i].length;
|
|
}
|
|
|
|
char[] str_array = AllocTypeArray!(char)(length);
|
|
|
|
uint pos;
|
|
static foreach(arg; args)
|
|
{
|
|
str_array[pos .. pos+arg.length] = arg[0 .. $];
|
|
pos += arg.length;
|
|
}
|
|
|
|
return cast(string)str_array;
|
|
}
|
|
|
|
class TypeInfo_StaticArray : TypeInfo
|
|
{
|
|
override string toString() const
|
|
{
|
|
import core.internal.string : unsignedToTempString;
|
|
|
|
char[20] tmpBuff = void;
|
|
|
|
const length_string = unsignedToTempString(len, tmpBuff);
|
|
const value_string = value.toString();
|
|
|
|
// return (() @trusted => cast(string) (value.toString() ~ "[" ~ lenString ~ "]"))();
|
|
|
|
return MakeString(value_string, "[", length_string, "]");
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_StaticArray)o;
|
|
return c && this.len == c.len &&
|
|
this.value == c.value;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
return getArrayHash(value, p, len);
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
size_t sz = value.tsize;
|
|
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
if (!value.equals(p1 + u * sz, p2 + u * sz))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
size_t sz = value.tsize;
|
|
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
immutable int result = value.compare(p1 + u * sz, p2 + u * sz);
|
|
if (result)
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return len * value.tsize;
|
|
}
|
|
|
|
override void swap(void* p1, void* p2) const
|
|
{
|
|
import core.stdc.string : memcpy;
|
|
|
|
size_t remaining = value.tsize * len;
|
|
void[size_t.sizeof * 4] buffer = void;
|
|
while (remaining > buffer.length)
|
|
{
|
|
memcpy(buffer.ptr, p1, buffer.length);
|
|
memcpy(p1, p2, buffer.length);
|
|
memcpy(p2, buffer.ptr, buffer.length);
|
|
p1 += buffer.length;
|
|
p2 += buffer.length;
|
|
remaining -= buffer.length;
|
|
}
|
|
memcpy(buffer.ptr, p1, remaining);
|
|
memcpy(p1, p2, remaining);
|
|
memcpy(p2, buffer.ptr, remaining);
|
|
}
|
|
|
|
override const(void)[] initializer() nothrow pure const
|
|
{
|
|
return value.initializer();
|
|
}
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
|
override @property uint flags() nothrow pure const { return value.flags; }
|
|
|
|
override void destroy(void* p) const
|
|
{
|
|
immutable sz = value.tsize;
|
|
p += sz * len;
|
|
foreach (i; 0 .. len)
|
|
{
|
|
p -= sz;
|
|
value.destroy(p);
|
|
}
|
|
}
|
|
|
|
override void postblit(void* p) const
|
|
{
|
|
immutable sz = value.tsize;
|
|
foreach (i; 0 .. len)
|
|
{
|
|
value.postblit(p);
|
|
p += sz;
|
|
}
|
|
}
|
|
|
|
TypeInfo value;
|
|
size_t len;
|
|
|
|
override @property size_t talign() nothrow pure const
|
|
{
|
|
return value.talign;
|
|
}
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(void*);
|
|
return 0;
|
|
}
|
|
|
|
// just return the rtInfo of the element, we have no generic type T to run RTInfo!T on
|
|
override @property immutable(void)* rtInfo() nothrow pure const @safe { return value.rtInfo(); }
|
|
}
|
|
|
|
/*
|
|
import core.arsd.aa;
|
|
alias AARange = core.arsd.aa.Range;
|
|
extern (C)
|
|
{
|
|
private alias AA = void*;
|
|
|
|
// size_t _aaLen(in AA aa) pure nothrow @nogc;
|
|
private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
|
|
private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) ;
|
|
// inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
|
|
inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) ;
|
|
inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) ;
|
|
void* _aaRehash(AA* paa, const scope TypeInfo keyti) ;
|
|
void _aaClear(AA aa) ;
|
|
|
|
// alias _dg_t = extern(D) int delegate(void*);
|
|
// int _aaApply(AA aa, size_t keysize, _dg_t dg);
|
|
|
|
// alias _dg2_t = extern(D) int delegate(void*, void*);
|
|
// int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
|
|
|
|
AARange _aaRange(AA aa) pure nothrow @nogc @safe;
|
|
bool _aaRangeEmpty(AARange r) pure @safe @nogc nothrow;
|
|
void* _aaRangeFrontKey(AARange r);
|
|
void* _aaRangeFrontValue(AARange r) pure @nogc nothrow;
|
|
void _aaRangePopFront(ref AARange r) pure @nogc nothrow @safe;
|
|
|
|
int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
|
|
size_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
|
|
|
|
void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values);
|
|
}
|
|
|
|
private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
|
|
{
|
|
// ensure we are dealing with a genuine AA.
|
|
static if (is(const(V[K]) == const(T)))
|
|
alias realAA = aa;
|
|
else
|
|
const(V[K]) realAA = aa;
|
|
return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
|
|
}
|
|
|
|
auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
|
|
{
|
|
import core.internal.traits : substInout;
|
|
|
|
static struct Result
|
|
{
|
|
AARange r;
|
|
|
|
pure nothrow @nogc:
|
|
@property bool empty() @safe { return _aaRangeEmpty(r); }
|
|
@property ref front() @trusted
|
|
{
|
|
return *cast(substInout!K*) _aaRangeFrontKey(r);
|
|
}
|
|
void popFront() @safe { _aaRangePopFront(r); }
|
|
@property Result save() { return this; }
|
|
}
|
|
|
|
return Result(_aaToRange(aa));
|
|
}
|
|
|
|
auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
|
|
{
|
|
return (*aa).byKey();
|
|
}
|
|
|
|
|
|
|
|
auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
|
|
{
|
|
import core.internal.traits : substInout;
|
|
|
|
static struct Result
|
|
{
|
|
AARange r;
|
|
|
|
pure nothrow @nogc:
|
|
@property bool empty() @safe { return _aaRangeEmpty(r); }
|
|
@property ref front() @trusted
|
|
{
|
|
return *cast(substInout!V*) _aaRangeFrontValue(r);
|
|
}
|
|
void popFront() @safe { _aaRangePopFront(r); }
|
|
@property Result save() { return this; }
|
|
}
|
|
|
|
return Result(_aaToRange(aa));
|
|
}
|
|
|
|
auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
|
|
{
|
|
return (*aa).byValue();
|
|
}
|
|
|
|
Key[] keys(T : Value[Key], Value, Key)(T aa) @property
|
|
{
|
|
// ensure we are dealing with a genuine AA.
|
|
static if (is(const(Value[Key]) == const(T)))
|
|
alias realAA = aa;
|
|
else
|
|
const(Value[Key]) realAA = aa;
|
|
auto res = () @trusted {
|
|
auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
|
|
return *cast(Key[]*)&a;
|
|
}();
|
|
static if (__traits(hasPostblit, Key))
|
|
_doPostblit(res);
|
|
return res;
|
|
}
|
|
|
|
Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
|
|
{
|
|
return (*aa).keys;
|
|
}
|
|
|
|
Value[] values(T : Value[Key], Value, Key)(T aa) @property
|
|
{
|
|
// ensure we are dealing with a genuine AA.
|
|
static if (is(const(Value[Key]) == const(T)))
|
|
alias realAA = aa;
|
|
else
|
|
const(Value[Key]) realAA = aa;
|
|
auto res = () @trusted {
|
|
auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
|
|
return *cast(Value[]*)&a;
|
|
}();
|
|
static if (__traits(hasPostblit, Value))
|
|
_doPostblit(res);
|
|
return res;
|
|
}
|
|
|
|
Value[] values(T : Value[Key], Value, Key)(T *aa) @property
|
|
{
|
|
return (*aa).values;
|
|
}
|
|
inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)
|
|
{
|
|
auto p = key in aa;
|
|
return p ? *p : defaultValue;
|
|
}
|
|
|
|
inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
|
|
{
|
|
return (*aa).get(key, defaultValue);
|
|
}
|
|
|
|
// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
|
|
private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
|
|
|
|
void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
|
|
if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
|
|
{
|
|
bool found;
|
|
// if key is @safe-ly copyable, `update` may infer @safe
|
|
static if (isSafeCopyable!K)
|
|
{
|
|
auto p = () @trusted
|
|
{
|
|
return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
|
|
} ();
|
|
}
|
|
else
|
|
{
|
|
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
|
|
}
|
|
if (!found)
|
|
*p = create();
|
|
else
|
|
{
|
|
static if (is(typeof(update(*p)) == void))
|
|
update(*p);
|
|
else
|
|
*p = update(*p);
|
|
}
|
|
}
|
|
|
|
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
|
|
{
|
|
bool found;
|
|
// if key is @safe-ly copyable, `require` can infer @safe
|
|
static if (isSafeCopyable!K)
|
|
{
|
|
auto p = () @trusted
|
|
{
|
|
return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
|
|
} ();
|
|
}
|
|
else
|
|
{
|
|
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
|
|
}
|
|
if (found)
|
|
return *p;
|
|
else
|
|
{
|
|
*p = value; // Not `return (*p = value)` since if `=` is overloaded
|
|
return *p; // this might not return a ref to the left-hand side.
|
|
}
|
|
}
|
|
|
|
void clear(Value, Key)(Value[Key] aa)
|
|
{
|
|
_aaClear(*cast(AA *) &aa);
|
|
}
|
|
|
|
void clear(Value, Key)(Value[Key]* aa)
|
|
{
|
|
_aaClear(*cast(AA *) aa);
|
|
}
|
|
|
|
void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
|
|
{
|
|
return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
|
|
}
|
|
|
|
alias AssociativeArray(Key, Value) = Value[Key];
|
|
|
|
class TypeInfo_AssociativeArray : TypeInfo
|
|
{
|
|
override string toString() const
|
|
{
|
|
return value.toString() ~ "[" ~ key.toString() ~ "]";
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_AssociativeArray)o;
|
|
return c && this.key == c.key &&
|
|
this.value == c.value;
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) @trusted const
|
|
{
|
|
return xopEquals(p1, p2);
|
|
}
|
|
|
|
override hash_t getHash(scope const void* p) nothrow @trusted const
|
|
{
|
|
return xtoHash(p);
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return (char[int]).sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. (char[int]).sizeof];
|
|
}
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
|
|
private static import core.internal.newaa;
|
|
alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
|
|
|
|
TypeInfo value;
|
|
TypeInfo key;
|
|
TypeInfo entry;
|
|
|
|
bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
|
|
hash_t function(scope const void*) nothrow @safe xtoHash;
|
|
|
|
alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
|
|
alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
|
|
|
|
override @property size_t talign() nothrow pure const
|
|
{
|
|
return (char[int]).alignof;
|
|
}
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(void*);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
*/
|
|
|
|
class TypeInfo_Enum : TypeInfo
|
|
{
|
|
override string toString() const pure { return name; }
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Enum)o;
|
|
return c && this.name == c.name &&
|
|
this.base == c.base;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) const { return base.getHash(p); }
|
|
|
|
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
|
|
|
|
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
|
|
|
|
override @property size_t tsize() nothrow pure const { return base.tsize; }
|
|
|
|
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
|
|
|
|
override @property uint flags() nothrow pure const { return base.flags; }
|
|
|
|
override const(OffsetTypeInfo)[] offTi() const { return base.offTi; }
|
|
|
|
override void destroy(void* p) const { return base.destroy(p); }
|
|
override void postblit(void* p) const { return base.postblit(p); }
|
|
|
|
override const(void)[] initializer() const
|
|
{
|
|
return m_init.length ? m_init : base.initializer();
|
|
}
|
|
|
|
override @property size_t talign() nothrow pure const { return base.talign; }
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
return base.argTypes(arg1, arg2);
|
|
}
|
|
|
|
override @property immutable(void)* rtInfo() const { return base.rtInfo; }
|
|
|
|
TypeInfo base;
|
|
string name;
|
|
void[] m_init;
|
|
}
|
|
|
|
enum
|
|
{
|
|
MIctorstart = 0x1, // we've started constructing it
|
|
MIctordone = 0x2, // finished construction
|
|
MIstandalone = 0x4, // module ctor does not depend on other module
|
|
// ctors being done first
|
|
MItlsctor = 8,
|
|
MItlsdtor = 0x10,
|
|
MIctor = 0x20,
|
|
MIdtor = 0x40,
|
|
MIxgetMembers = 0x80,
|
|
MIictor = 0x100,
|
|
MIunitTest = 0x200,
|
|
MIimportedModules = 0x400,
|
|
MIlocalClasses = 0x800,
|
|
MIname = 0x1000,
|
|
}
|
|
|
|
struct ModuleInfo
|
|
{
|
|
uint _flags; // MIxxxx
|
|
uint _index; // index into _moduleinfo_array[]
|
|
|
|
version (all)
|
|
{
|
|
deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.")
|
|
void opAssign(const scope ModuleInfo m) { _flags = m._flags; _index = m._index; }
|
|
}
|
|
else
|
|
{
|
|
@disable this();
|
|
}
|
|
|
|
const:
|
|
private void* addrOf(int flag) return nothrow pure @nogc
|
|
in
|
|
{
|
|
assert(flag >= MItlsctor && flag <= MIname);
|
|
assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1));
|
|
}
|
|
do
|
|
{
|
|
import core.stdc.string : strlen;
|
|
|
|
void* p = cast(void*)&this + ModuleInfo.sizeof;
|
|
|
|
if (flags & MItlsctor)
|
|
{
|
|
if (flag == MItlsctor) return p;
|
|
p += typeof(tlsctor).sizeof;
|
|
}
|
|
if (flags & MItlsdtor)
|
|
{
|
|
if (flag == MItlsdtor) return p;
|
|
p += typeof(tlsdtor).sizeof;
|
|
}
|
|
if (flags & MIctor)
|
|
{
|
|
if (flag == MIctor) return p;
|
|
p += typeof(ctor).sizeof;
|
|
}
|
|
if (flags & MIdtor)
|
|
{
|
|
if (flag == MIdtor) return p;
|
|
p += typeof(dtor).sizeof;
|
|
}
|
|
if (flags & MIxgetMembers)
|
|
{
|
|
if (flag == MIxgetMembers) return p;
|
|
p += typeof(xgetMembers).sizeof;
|
|
}
|
|
if (flags & MIictor)
|
|
{
|
|
if (flag == MIictor) return p;
|
|
p += typeof(ictor).sizeof;
|
|
}
|
|
if (flags & MIunitTest)
|
|
{
|
|
if (flag == MIunitTest) return p;
|
|
p += typeof(unitTest).sizeof;
|
|
}
|
|
if (flags & MIimportedModules)
|
|
{
|
|
if (flag == MIimportedModules) return p;
|
|
p += size_t.sizeof + *cast(size_t*)p * typeof(importedModules[0]).sizeof;
|
|
}
|
|
if (flags & MIlocalClasses)
|
|
{
|
|
if (flag == MIlocalClasses) return p;
|
|
p += size_t.sizeof + *cast(size_t*)p * typeof(localClasses[0]).sizeof;
|
|
}
|
|
if (true || flags & MIname) // always available for now
|
|
{
|
|
if (flag == MIname) return p;
|
|
p += strlen(cast(immutable char*)p);
|
|
}
|
|
assert(0);
|
|
}
|
|
|
|
@property uint index() nothrow pure @nogc { return _index; }
|
|
|
|
@property uint flags() nothrow pure @nogc { return _flags; }
|
|
|
|
/************************
|
|
* Returns:
|
|
* module constructor for thread locals, `null` if there isn't one
|
|
*/
|
|
@property void function() tlsctor() nothrow pure @nogc
|
|
{
|
|
return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null;
|
|
}
|
|
|
|
/************************
|
|
* Returns:
|
|
* module destructor for thread locals, `null` if there isn't one
|
|
*/
|
|
@property void function() tlsdtor() nothrow pure @nogc
|
|
{
|
|
return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null;
|
|
}
|
|
|
|
/*****************************
|
|
* Returns:
|
|
* address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one
|
|
*/
|
|
@property void* xgetMembers() nothrow pure @nogc
|
|
{
|
|
return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null;
|
|
}
|
|
|
|
/************************
|
|
* Returns:
|
|
* module constructor, `null` if there isn't one
|
|
*/
|
|
@property void function() ctor() nothrow pure @nogc
|
|
{
|
|
return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null;
|
|
}
|
|
|
|
/************************
|
|
* Returns:
|
|
* module destructor, `null` if there isn't one
|
|
*/
|
|
@property void function() dtor() nothrow pure @nogc
|
|
{
|
|
return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null;
|
|
}
|
|
|
|
/************************
|
|
* Returns:
|
|
* module order independent constructor, `null` if there isn't one
|
|
*/
|
|
@property void function() ictor() nothrow pure @nogc
|
|
{
|
|
return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null;
|
|
}
|
|
|
|
/*************
|
|
* Returns:
|
|
* address of function that runs the module's unittests, `null` if there isn't one
|
|
*/
|
|
@property void function() unitTest() nothrow pure @nogc
|
|
{
|
|
return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null;
|
|
}
|
|
|
|
/****************
|
|
* Returns:
|
|
* array of pointers to the ModuleInfo's of modules imported by this one
|
|
*/
|
|
@property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc
|
|
{
|
|
if (flags & MIimportedModules)
|
|
{
|
|
auto p = cast(size_t*)addrOf(MIimportedModules);
|
|
return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/****************
|
|
* Returns:
|
|
* array of TypeInfo_Class references for classes defined in this module
|
|
*/
|
|
@property TypeInfo_Class[] localClasses() return nothrow pure @nogc
|
|
{
|
|
if (flags & MIlocalClasses)
|
|
{
|
|
auto p = cast(size_t*)addrOf(MIlocalClasses);
|
|
return (cast(TypeInfo_Class*)(p + 1))[0 .. *p];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/********************
|
|
* Returns:
|
|
* name of module, `null` if no name
|
|
*/
|
|
@property string name() return nothrow pure @nogc
|
|
{
|
|
import core.stdc.string : strlen;
|
|
|
|
auto p = cast(immutable char*) addrOf(MIname);
|
|
return p[0 .. cast(size_t)strlen(p)];
|
|
}
|
|
|
|
static int opApply(scope int delegate(ModuleInfo*) dg)
|
|
{
|
|
/*
|
|
import core.internal.traits : externDFunc;
|
|
alias moduleinfos_apply = externDFunc!("rt.minfo.moduleinfos_apply",
|
|
int function(scope int delegate(immutable(ModuleInfo*))));
|
|
// Bugzilla 13084 - enforcing immutable ModuleInfo would break client code
|
|
return moduleinfos_apply(
|
|
(immutable(ModuleInfo*)m) => dg(cast(ModuleInfo*)m));
|
|
*/
|
|
|
|
assert(false, "Not implemented for platform");
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length)
|
|
{
|
|
return AllocTypeArray!(ubyte)(length * ti.next.tsize);
|
|
}
|
|
|
|
extern(C) void[] _d_newarrayT(const TypeInfo ti, size_t length)
|
|
{
|
|
auto arr = _d_newarrayU(ti, length);
|
|
(cast(byte[])arr)[] = 0;
|
|
return arr;
|
|
}
|
|
|
|
extern(C) void[] _d_newarrayiT(const TypeInfo ti, size_t length)
|
|
{
|
|
auto result = _d_newarrayU(ti, length);
|
|
auto tinext = ti.next;
|
|
auto size = tinext.tsize;
|
|
auto init = tinext.initializer();
|
|
switch (init.length)
|
|
{
|
|
foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
|
|
{
|
|
case T.sizeof:
|
|
if (tinext.talign % T.alignof == 0)
|
|
{
|
|
(cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
|
|
return result;
|
|
}
|
|
goto default;
|
|
}
|
|
|
|
default:
|
|
{
|
|
immutable sz = init.length;
|
|
for (size_t u = 0; u < size * length; u += sz)
|
|
{
|
|
memcpy(result.ptr + u, init.ptr, sz);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
extern (C) void* _d_newitemU(scope const TypeInfo _ti)
|
|
{
|
|
auto ti = unqualify(_ti);
|
|
return malloc(ti.tsize);
|
|
}
|
|
*/
|
|
|
|
/// ditto
|
|
extern (C) T* _d_newitemT(T)() @trusted
|
|
{
|
|
T* p = cast(T*)malloc(T.sizeof);
|
|
memset(p, 0, T.sizeof);
|
|
|
|
emplaceInitializer(*p);
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
/// Same as above, for item with non-zero initializer.
|
|
extern (C) void* _d_newitemiT(TypeInfo ti)
|
|
{
|
|
auto p = _d_newitemU(ti);
|
|
const initializer = ti.initializer;
|
|
memcpy(p, initializer.ptr, initializer.length);
|
|
return p;
|
|
}
|
|
*/
|
|
|
|
private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
|
|
{
|
|
if (dimensions.length == 0)
|
|
return null;
|
|
|
|
void[] foo(const TypeInfo ti, size_t[] dimensions)
|
|
{
|
|
size_t count = dimensions[0];
|
|
|
|
if (dimensions.length == 1)
|
|
{
|
|
auto r = op(ti, count);
|
|
return (*cast(void[]*)(&r))[0..count];
|
|
}
|
|
|
|
size_t size = (void[]).sizeof*count;
|
|
void[] p = malloc(size)[0 .. size];
|
|
|
|
foreach (i; 0 .. count)
|
|
{
|
|
(cast(void[]*)p.ptr)[i] = foo(ti.next, dimensions[1 .. $]);
|
|
}
|
|
|
|
return p[0 .. count];
|
|
}
|
|
|
|
return foo(ti, dimensions);
|
|
}
|
|
|
|
|
|
extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims)
|
|
{
|
|
if (dims.length == 0)
|
|
return null;
|
|
else
|
|
return _d_newarrayOpT!(_d_newarrayT)(ti, dims);
|
|
}
|
|
|
|
/// ditto
|
|
extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
|
|
{
|
|
if (dims.length == 0)
|
|
return null;
|
|
else
|
|
return _d_newarrayOpT!(_d_newarrayiT)(ti, dims);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
AllocatedBlock* getAllocatedBlock(void* ptr) {
|
|
auto block = (cast(AllocatedBlock*) ptr) - 1;
|
|
if(!block.checkChecksum())
|
|
return null;
|
|
return block;
|
|
}
|
|
|
|
void assumeSafeAppend(T)(T[] arr) {
|
|
auto block = getAllocatedBlock(arr.ptr);
|
|
if(block is null) assert(0);
|
|
|
|
block.used = arr.length;
|
|
}
|
|
*/
|
|
|
|
/++
|
|
Marks the memory block associated with this array as unique, meaning
|
|
the runtime is allowed to free the old block immediately instead of
|
|
keeping it around for other lingering slices.
|
|
|
|
In real D, the GC would take care of this but here I have to hack it.
|
|
|
|
arsd.webasm extension
|
|
+/
|
|
void assumeUniqueReference(T)(T[] arr) {
|
|
auto block = getAllocatedBlock(arr.ptr);
|
|
if(block is null) assert(0);
|
|
|
|
block.flags |= AllocatedBlock.Flags.unique;
|
|
}
|
|
|
|
template _d_arraysetlengthTImpl(Tarr : T[], T) {
|
|
size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted {
|
|
auto orig = arr;
|
|
|
|
if(newlength <= arr.length) {
|
|
arr = arr[0 ..newlength];
|
|
} else {
|
|
auto ptr = cast(T*) realloc(cast(ubyte[])arr, newlength * T.sizeof);
|
|
arr = ptr[0 .. newlength];
|
|
if(orig !is null) {
|
|
arr[0 .. orig.length] = orig[];
|
|
}
|
|
}
|
|
|
|
return newlength;
|
|
}
|
|
}
|
|
|
|
/*
|
|
extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) @trusted {
|
|
auto elemSize = ti.next.size;
|
|
auto newLength = n + px.length;
|
|
auto newSize = newLength * elemSize;
|
|
//import std.stdio; writeln(newSize, " ", newLength);
|
|
ubyte* ptr;
|
|
if(px.ptr is null)
|
|
ptr = malloc(newSize).ptr;
|
|
else // FIXME: anti-stomping by checking length == used
|
|
ptr = realloc(cast(ubyte[])px, newSize).ptr;
|
|
auto ns = ptr[0 .. newSize];
|
|
auto op = px.ptr;
|
|
auto ol = px.length * elemSize;
|
|
|
|
foreach(i, b; op[0 .. ol])
|
|
ns[i] = b;
|
|
|
|
(cast(size_t *)(&px))[0] = newLength;
|
|
(cast(void **)(&px))[1] = ns.ptr;
|
|
return px;
|
|
}
|
|
*/
|
|
|
|
version(inline_concat)
|
|
extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
|
|
{
|
|
import core.arsd.objectutils;
|
|
auto sizeelem = ti.next.tsize; // array element size
|
|
size_t xlen = x.length * sizeelem;
|
|
size_t ylen = y.length * sizeelem;
|
|
size_t len = xlen + ylen;
|
|
|
|
if (!len)
|
|
return null;
|
|
|
|
byte[] p = AllocTypeArray!(byte)(len);
|
|
memcpy(p.ptr, x.ptr, xlen);
|
|
memcpy(p.ptr + xlen, y.ptr, ylen);
|
|
// do postblit processing
|
|
__doPostblit(p.ptr, xlen + ylen, ti.next);
|
|
return p[0 .. x.length + y.length];
|
|
}
|
|
|
|
/*
|
|
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
|
|
{
|
|
// c could encode into from 1 to 4 characters
|
|
char[4] buf = void;
|
|
byte[] appendthis; // passed to appendT
|
|
if (c <= 0x7F)
|
|
{
|
|
buf.ptr[0] = cast(char)c;
|
|
appendthis = (cast(byte *)buf.ptr)[0..1];
|
|
}
|
|
else if (c <= 0x7FF)
|
|
{
|
|
buf.ptr[0] = cast(char)(0xC0 | (c >> 6));
|
|
buf.ptr[1] = cast(char)(0x80 | (c & 0x3F));
|
|
appendthis = (cast(byte *)buf.ptr)[0..2];
|
|
}
|
|
else if (c <= 0xFFFF)
|
|
{
|
|
buf.ptr[0] = cast(char)(0xE0 | (c >> 12));
|
|
buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
|
|
buf.ptr[2] = cast(char)(0x80 | (c & 0x3F));
|
|
appendthis = (cast(byte *)buf.ptr)[0..3];
|
|
}
|
|
else if (c <= 0x10FFFF)
|
|
{
|
|
buf.ptr[0] = cast(char)(0xF0 | (c >> 18));
|
|
buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
|
|
buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
|
|
buf.ptr[3] = cast(char)(0x80 | (c & 0x3F));
|
|
appendthis = (cast(byte *)buf.ptr)[0..4];
|
|
}
|
|
else
|
|
assert(false, "Could not append dchar"); // invalid utf character - should we throw an exception instead?
|
|
|
|
//
|
|
// TODO: This always assumes the array type is shared, because we do not
|
|
// get a typeinfo from the compiler. Assuming shared is the safest option.
|
|
// Once the compiler is fixed, the proper typeinfo should be forwarded.
|
|
//
|
|
return _d_arrayappendT(typeid(shared char[]), x, appendthis);
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
/* TODO: see if needed
|
|
static foreach(type; AliasSeq!(byte, char, dchar, double, float, int, long, short, ubyte, uint, ulong, ushort, void, wchar)) {
|
|
mixin(q{
|
|
class TypeInfo_}~type.mangleof~q{ : TypeInfo {
|
|
override string toString() const pure nothrow @safe { return type.stringof; }
|
|
override size_t size() const { return type.sizeof; }
|
|
override @property size_t talign() const pure nothrow
|
|
{
|
|
return type.alignof;
|
|
}
|
|
|
|
override bool equals(in void* a, in void* b) const {
|
|
static if(is(type == void))
|
|
return false;
|
|
else
|
|
return (*(cast(type*) a) == (*(cast(type*) b)));
|
|
}
|
|
static if(!is(type == void))
|
|
override size_t getHash(scope const void* p) @trusted const nothrow
|
|
{
|
|
return hashOf(*cast(const type *)p);
|
|
}
|
|
override const(void)[] initializer() pure nothrow @trusted const
|
|
{
|
|
static if(__traits(isZeroInit, type))
|
|
return (cast(void*)null)[0 .. type.sizeof];
|
|
else
|
|
{
|
|
static immutable type[1] c;
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
class TypeInfo_A}~type.mangleof~q{ : TypeInfo_Array {
|
|
override string toString() const { return (type[]).stringof; }
|
|
override const(TypeInfo) next() const { return cast(inout)typeid(type); }
|
|
override size_t getHash(scope const void* p) @trusted const nothrow
|
|
{
|
|
return hashOf(*cast(const type[]*) p);
|
|
}
|
|
|
|
override bool equals(in void* av, in void* bv) const {
|
|
type[] a = *(cast(type[]*) av);
|
|
type[] b = *(cast(type[]*) bv);
|
|
|
|
static if(is(type == void))
|
|
return false;
|
|
else {
|
|
foreach(idx, item; a)
|
|
if(item != b[idx])
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
*/
|
|
|
|
struct Interface {
|
|
TypeInfo_Class classinfo;
|
|
void*[] vtbl;
|
|
size_t offset;
|
|
}
|
|
|
|
/**
|
|
* Array of pairs giving the offset and type information for each
|
|
* member in an aggregate.
|
|
*/
|
|
struct OffsetTypeInfo
|
|
{
|
|
size_t offset; /// Offset of member from start of object
|
|
TypeInfo ti; /// TypeInfo for this member
|
|
}
|
|
|
|
class TypeInfo_Function : TypeInfo
|
|
{
|
|
override string toString() const pure @trusted
|
|
{
|
|
/*
|
|
import core.demangle : demangleType;
|
|
|
|
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
|
|
SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
|
|
|
|
return cast(string) demangle(deco);
|
|
*/
|
|
|
|
assert(false, "Not implemented for platform");
|
|
|
|
return null;
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Function)o;
|
|
return c && this.deco == c.deco;
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return 0; // no size for functions
|
|
}
|
|
|
|
override const(void)[] initializer() const @safe
|
|
{
|
|
return null;
|
|
}
|
|
|
|
override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; }
|
|
|
|
TypeInfo next;
|
|
|
|
/**
|
|
* Mangled function type string
|
|
*/
|
|
string deco;
|
|
}
|
|
|
|
class TypeInfo_Delegate : TypeInfo
|
|
{
|
|
override string toString() const pure @trusted
|
|
{
|
|
/*
|
|
import core.demangle : demangleType;
|
|
|
|
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
|
|
SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
|
|
|
|
return cast(string) demangle(deco);
|
|
*/
|
|
|
|
assert(false, "Not implemented for platform");
|
|
|
|
return null;
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Delegate)o;
|
|
return c && this.deco == c.deco;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
return hashOf(*cast(const void delegate() *)p);
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
auto dg1 = *cast(void delegate()*)p1;
|
|
auto dg2 = *cast(void delegate()*)p2;
|
|
return dg1 == dg2;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
auto dg1 = *cast(void delegate()*)p1;
|
|
auto dg2 = *cast(void delegate()*)p2;
|
|
|
|
if (dg1 < dg2)
|
|
return -1;
|
|
else if (dg1 > dg2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
alias dg = int delegate();
|
|
return dg.sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. (int delegate()).sizeof];
|
|
}
|
|
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
TypeInfo next;
|
|
string deco;
|
|
|
|
override @property size_t talign() nothrow pure const
|
|
{
|
|
alias dg = int delegate();
|
|
return dg.alignof;
|
|
}
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(void*);
|
|
arg2 = typeid(void*);
|
|
return 0;
|
|
}
|
|
|
|
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(int delegate()); }
|
|
}
|
|
|
|
class TypeInfo_Interface : TypeInfo
|
|
{
|
|
override string toString() const pure { return info.name; }
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto c = cast(const TypeInfo_Interface)o;
|
|
return c && this.info.name == typeid(c).name;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
if (!*cast(void**)p)
|
|
{
|
|
return 0;
|
|
}
|
|
Interface* pi = **cast(Interface ***)*cast(void**)p;
|
|
Object o = cast(Object)(*cast(void**)p - pi.offset);
|
|
assert(o);
|
|
return o.toHash();
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
Interface* pi = **cast(Interface ***)*cast(void**)p1;
|
|
Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
|
|
pi = **cast(Interface ***)*cast(void**)p2;
|
|
Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
|
|
|
|
return o1 == o2 || (o1 && o1.opCmp(o2) == 0);
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
Interface* pi = **cast(Interface ***)*cast(void**)p1;
|
|
Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
|
|
pi = **cast(Interface ***)*cast(void**)p2;
|
|
Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
|
|
int c = 0;
|
|
|
|
// Regard null references as always being "less than"
|
|
if (o1 != o2)
|
|
{
|
|
if (o1)
|
|
{
|
|
if (!o2)
|
|
c = 1;
|
|
else
|
|
c = o1.opCmp(o2);
|
|
}
|
|
else
|
|
c = -1;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return Object.sizeof;
|
|
}
|
|
|
|
override const(void)[] initializer() const @trusted
|
|
{
|
|
return (cast(void *)null)[0 .. Object.sizeof];
|
|
}
|
|
|
|
override @property uint flags() nothrow pure const { return 1; }
|
|
|
|
TypeInfo_Class info;
|
|
|
|
/**
|
|
* Returns true if the class described by `child` derives from the
|
|
* interface described by this `TypeInfo_Interface`. Always returns
|
|
* false if the argument is null.
|
|
*
|
|
* Params:
|
|
* child = TypeInfo for some class
|
|
* Returns:
|
|
* true if the class described by `child` derives from the
|
|
* interface described by this `TypeInfo_Interface`.
|
|
*/
|
|
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
|
|
{
|
|
return child !is null && _d_isbaseof(cast() child, this.info);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the interface described by `child` derives from
|
|
* or is the interface described by this `TypeInfo_Interface`.
|
|
* Always returns false if the argument is null.
|
|
*
|
|
* Params:
|
|
* child = TypeInfo for some interface
|
|
* Returns:
|
|
* true if the interface described by `child` derives from or is
|
|
* the interface described by this `TypeInfo_Interface`.
|
|
*/
|
|
final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
|
|
{
|
|
return child !is null && _d_isbaseof(cast() child.info, this.info);
|
|
}
|
|
}
|
|
|
|
class TypeInfo_Const : TypeInfo
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString("const(", base.toString(), ")");
|
|
}
|
|
|
|
//override bool opEquals(Object o) { return base.opEquals(o); }
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
|
|
if (typeid(this) != typeid(o))
|
|
return false;
|
|
|
|
auto t = cast(TypeInfo_Const)o;
|
|
return base.opEquals(t.base);
|
|
}
|
|
|
|
override size_t getHash(scope const void *p) const { return base.getHash(p); }
|
|
override bool equals(in void *p1, in void *p2) const { return base.equals(p1, p2); }
|
|
override int compare(in void *p1, in void *p2) const { return base.compare(p1, p2); }
|
|
override @property size_t tsize() nothrow pure const { return base.tsize; }
|
|
override void swap(void *p1, void *p2) const { return base.swap(p1, p2); }
|
|
|
|
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
|
|
override @property uint flags() nothrow pure const { return base.flags; }
|
|
|
|
override const(void)[] initializer() nothrow pure const
|
|
{
|
|
return base.initializer();
|
|
}
|
|
|
|
override @property size_t talign() nothrow pure const { return base.talign; }
|
|
|
|
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
return base.argTypes(arg1, arg2);
|
|
}
|
|
|
|
TypeInfo base;
|
|
}
|
|
|
|
///For some reason, getHash for interfaces wanted that
|
|
pragma(mangle, "_D2rt10invariant_12_d_invariantFC6ObjectZv")
|
|
extern(D) void _d_invariant(Object o)
|
|
{
|
|
TypeInfo_Class c;
|
|
|
|
//printf("__d_invariant(%p)\n", o);
|
|
|
|
// BUG: needs to be filename/line of caller, not library routine
|
|
assert(o !is null); // just do null check, not invariant check
|
|
|
|
c = typeid(o);
|
|
do
|
|
{
|
|
if (c.classInvariant)
|
|
{
|
|
(*c.classInvariant)(o);
|
|
}
|
|
c = c.base;
|
|
} while (c);
|
|
}
|
|
|
|
/+
|
|
class TypeInfo_Immutable : TypeInfo {
|
|
size_t getHash(in void*) nothrow { return 0; }
|
|
TypeInfo base;
|
|
}
|
|
+/
|
|
class TypeInfo_Invariant : TypeInfo_Const
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString("immutable(", base.toString(), ")");
|
|
}
|
|
}
|
|
|
|
class TypeInfo_Shared : TypeInfo_Const
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString("shared(", base.toString(), ")");
|
|
}
|
|
}
|
|
|
|
class TypeInfo_Inout : TypeInfo_Const
|
|
{
|
|
override string toString() const
|
|
{
|
|
return MakeString("inout(", base.toString(), ")");
|
|
}
|
|
}
|
|
|
|
class TypeInfo_Struct : TypeInfo
|
|
{
|
|
override string toString() const { return name; }
|
|
|
|
override size_t toHash() const
|
|
{
|
|
return hashOf(this.mangledName);
|
|
}
|
|
|
|
override bool opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
auto s = cast(const TypeInfo_Struct)o;
|
|
return s && this.mangledName == s.mangledName;
|
|
}
|
|
|
|
override size_t getHash(scope const void* p) @trusted pure nothrow const
|
|
{
|
|
assert(p);
|
|
if (xtoHash)
|
|
{
|
|
return (*xtoHash)(p);
|
|
}
|
|
else
|
|
{
|
|
return hashOf(p[0 .. initializer().length]);
|
|
}
|
|
}
|
|
|
|
override bool equals(in void* p1, in void* p2) @trusted pure nothrow const
|
|
{
|
|
import core.stdc.string : memcmp;
|
|
|
|
if (!p1 || !p2)
|
|
return false;
|
|
else if (xopEquals)
|
|
{
|
|
const dg = _memberFunc(p1, xopEquals);
|
|
return dg.xopEquals(p2);
|
|
}
|
|
else if (p1 == p2)
|
|
return true;
|
|
else
|
|
// BUG: relies on the GC not moving objects
|
|
return memcmp(p1, p2, initializer().length) == 0;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2) @trusted pure nothrow const
|
|
{
|
|
import core.stdc.string : memcmp;
|
|
|
|
// Regard null references as always being "less than"
|
|
if (p1 != p2)
|
|
{
|
|
if (p1)
|
|
{
|
|
if (!p2)
|
|
return true;
|
|
else if (xopCmp)
|
|
{
|
|
const dg = _memberFunc(p1, xopCmp);
|
|
return dg.xopCmp(p2);
|
|
}
|
|
else
|
|
// BUG: relies on the GC not moving objects
|
|
return memcmp(p1, p2, initializer().length);
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
override @property size_t tsize() nothrow pure const
|
|
{
|
|
return initializer().length;
|
|
}
|
|
|
|
override const(void)[] initializer() nothrow pure const @safe
|
|
{
|
|
return m_init;
|
|
}
|
|
|
|
override @property uint flags() nothrow pure const { return m_flags; }
|
|
|
|
override @property size_t talign() nothrow pure const { return m_align; }
|
|
|
|
final override void destroy(void* p) const
|
|
{
|
|
if (xdtor)
|
|
{
|
|
if (m_flags & StructFlags.isDynamicType)
|
|
(*xdtorti)(p, this);
|
|
else
|
|
(*xdtor)(p);
|
|
}
|
|
}
|
|
|
|
override void postblit(void* p) const
|
|
{
|
|
if (xpostblit)
|
|
(*xpostblit)(p);
|
|
}
|
|
|
|
string mangledName;
|
|
|
|
final @property string name() nothrow const @trusted
|
|
{
|
|
/*
|
|
import core.demangle : demangleType;
|
|
|
|
if (mangledName is null) // e.g., opaque structs
|
|
return null;
|
|
|
|
const key = cast(const void*) this; // faster lookup than TypeInfo_Struct, at the cost of potential duplicates per binary
|
|
static string[typeof(key)] demangledNamesCache; // per thread
|
|
|
|
// not nothrow:
|
|
//return demangledNamesCache.require(key, cast(string) demangleType(mangledName));
|
|
|
|
if (auto pDemangled = key in demangledNamesCache)
|
|
return *pDemangled;
|
|
|
|
const demangled = cast(string) demangleType(mangledName);
|
|
demangledNamesCache[key] = demangled;
|
|
return demangled;
|
|
*/
|
|
|
|
assert(false, "Not implemented for platform");
|
|
|
|
return null;
|
|
}
|
|
|
|
void[] m_init; // initializer; m_init.ptr == null if 0 initialize
|
|
|
|
@safe pure nothrow
|
|
{
|
|
size_t function(in void*) xtoHash;
|
|
bool function(in void*, in void*) xopEquals;
|
|
int function(in void*, in void*) xopCmp;
|
|
string function(in void*) xtoString;
|
|
|
|
enum StructFlags : uint
|
|
{
|
|
hasPointers = 0x1,
|
|
isDynamicType = 0x2, // built at runtime, needs type info in xdtor
|
|
}
|
|
StructFlags m_flags;
|
|
}
|
|
union
|
|
{
|
|
void function(void*) xdtor;
|
|
void function(void*, const TypeInfo_Struct ti) xdtorti;
|
|
}
|
|
void function(void*) xpostblit;
|
|
|
|
uint m_align;
|
|
|
|
override @property immutable(void)* rtInfo() nothrow pure const @safe { return m_RTInfo; }
|
|
|
|
version (WithArgTypes)
|
|
{
|
|
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = m_arg1;
|
|
arg2 = m_arg2;
|
|
return 0;
|
|
}
|
|
TypeInfo m_arg1;
|
|
TypeInfo m_arg2;
|
|
}
|
|
immutable(void)* m_RTInfo; // data for precise GC
|
|
|
|
// The xopEquals and xopCmp members are function pointers to member
|
|
// functions, which is not guaranteed to share the same ABI, as it is not
|
|
// known whether the `this` parameter is the first or second argument.
|
|
// This wrapper is to convert it to a delegate which will always pass the
|
|
// `this` parameter in the correct way.
|
|
private struct _memberFunc
|
|
{
|
|
union
|
|
{
|
|
struct // delegate
|
|
{
|
|
const void* ptr;
|
|
const void* funcptr;
|
|
}
|
|
@safe pure nothrow
|
|
{
|
|
bool delegate(in void*) xopEquals;
|
|
int delegate(in void*) xopCmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _xopEquals(in void*, in void*)
|
|
{
|
|
assert(false, "TypeInfo.equals is not implemented");
|
|
}
|
|
|
|
bool _xopCmp(in void*, in void*)
|
|
{
|
|
assert(false, "TypeInfo.compare is not implemented");
|
|
}
|
|
|
|
// }
|
|
|
|
void __ArrayDtor(T)(scope T[] a)
|
|
{
|
|
foreach_reverse (ref T e; a)
|
|
e.__xdtor();
|
|
}
|
|
|
|
TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) nothrow
|
|
{
|
|
const fromSize = from.length * TFrom.sizeof;
|
|
const toLength = fromSize / TTo.sizeof;
|
|
|
|
if ((fromSize % TTo.sizeof) != 0)
|
|
{
|
|
//onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
|
|
abort();
|
|
}
|
|
|
|
struct Array
|
|
{
|
|
size_t length;
|
|
void* ptr;
|
|
}
|
|
auto a = cast(Array*)&from;
|
|
a.length = toLength; // jam new length
|
|
return *cast(TTo[]*)a;
|
|
}
|
|
|
|
extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y)
|
|
{
|
|
/*
|
|
auto length = x.length;
|
|
auto tinext = ti.next;
|
|
auto sizeelem = tinext.tsize; // array element size
|
|
_d_arrayappendcTX(ti, x, y.length);
|
|
memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem);
|
|
|
|
// do postblit
|
|
//__doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext);
|
|
*/
|
|
|
|
assert(false, "Not implemented for platform");
|
|
|
|
return x;
|
|
}
|
|
|
|
extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
|
|
{
|
|
debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2. length);
|
|
if (a1.length != a2.length)
|
|
return 0; // not equal
|
|
if (!ti.equals(&a1, &a2))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
V[K] dup(T : V[K], K, V)(T aa)
|
|
{
|
|
//pragma(msg, "K = ", K, ", V = ", V);
|
|
|
|
// Bug10720 - check whether V is copyable
|
|
static assert(is(typeof({ V v = aa[K.init]; })),
|
|
"cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
|
|
|
|
V[K] result;
|
|
|
|
//foreach (k, ref v; aa)
|
|
// result[k] = v; // Bug13701 - won't work if V is not mutable
|
|
|
|
ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
|
|
{
|
|
void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
|
|
memcpy(pv, &v, V.sizeof);
|
|
return *cast(V*)pv;
|
|
}
|
|
|
|
foreach (k, ref v; aa)
|
|
{
|
|
static if (!__traits(hasPostblit, V))
|
|
duplicateElem(k, v);
|
|
else static if (__traits(isStaticArray, V))
|
|
_doPostblit(duplicateElem(k, v)[]);
|
|
else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
|
|
(() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
|
|
else
|
|
duplicateElem(k, v).__xpostblit();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** ditto */
|
|
V[K] dup(T : V[K], K, V)(T* aa)
|
|
{
|
|
assert(false, "Not implemented for platform");
|
|
return (*aa).dup;
|
|
}
|
|
|
|
T[] dup(T)(scope T[] array) pure nothrow @trusted if (__traits(isPOD, T) && !is(const(T) : T))
|
|
{
|
|
T[] result;
|
|
foreach(ref e; array) {
|
|
result ~= e;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
T[] dup(T)(scope const(T)[] array) pure nothrow @trusted if (__traits(isPOD, T))
|
|
{
|
|
T[] result;
|
|
foreach(ref e; array) {
|
|
result ~= e;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
immutable(T)[] idup(T)(scope const(T)[] array) pure nothrow @trusted
|
|
{
|
|
immutable(T)[] result;
|
|
foreach(ref e; array) {
|
|
result ~= e;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class Error { this(string msg) {} }
|
|
class Throwable : Object
|
|
{
|
|
interface TraceInfo
|
|
{
|
|
int opApply(scope int delegate(ref const(char[]))) const;
|
|
int opApply(scope int delegate(ref size_t, ref const(char[]))) const;
|
|
string toString() const;
|
|
}
|
|
|
|
string msg; /// A message describing the error.
|
|
|
|
/**
|
|
* The _file name of the D source code corresponding with
|
|
* where the error was thrown from.
|
|
*/
|
|
string file;
|
|
/**
|
|
* The _line number of the D source code corresponding with
|
|
* where the error was thrown from.
|
|
*/
|
|
size_t line;
|
|
|
|
/**
|
|
* The stack trace of where the error happened. This is an opaque object
|
|
* that can either be converted to $(D string), or iterated over with $(D
|
|
* foreach) to extract the items in the stack trace (as strings).
|
|
*/
|
|
TraceInfo info;
|
|
|
|
/**
|
|
* A reference to the _next error in the list. This is used when a new
|
|
* $(D Throwable) is thrown from inside a $(D catch) block. The originally
|
|
* caught $(D Exception) will be chained to the new $(D Throwable) via this
|
|
* field.
|
|
*/
|
|
private Throwable nextInChain;
|
|
|
|
private uint _refcount; // 0 : allocated by GC
|
|
// 1 : allocated by _d_newThrowable()
|
|
// 2.. : reference count + 1
|
|
|
|
/**
|
|
* Returns:
|
|
* A reference to the _next error in the list. This is used when a new
|
|
* $(D Throwable) is thrown from inside a $(D catch) block. The originally
|
|
* caught $(D Exception) will be chained to the new $(D Throwable) via this
|
|
* field.
|
|
*/
|
|
@property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
|
|
|
|
/**
|
|
* Replace next in chain with `tail`.
|
|
* Use `chainTogether` instead if at all possible.
|
|
*/
|
|
@property void next(Throwable tail) @safe scope pure nothrow @nogc{}
|
|
|
|
/**
|
|
* Returns:
|
|
* mutable reference to the reference count, which is
|
|
* 0 - allocated by the GC, 1 - allocated by _d_newThrowable(),
|
|
* and >=2 which is the reference count + 1
|
|
* Note:
|
|
* Marked as `@system` to discourage casual use of it.
|
|
*/
|
|
@system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
|
|
|
|
/**
|
|
* Loop over the chain of Throwables.
|
|
*/
|
|
int opApply(scope int delegate(Throwable) dg)
|
|
{
|
|
int result = 0;
|
|
for (Throwable t = this; t; t = t.nextInChain)
|
|
{
|
|
result = dg(t);
|
|
if (result)
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Append `e2` to chain of exceptions that starts with `e1`.
|
|
* Params:
|
|
* e1 = start of chain (can be null)
|
|
* e2 = second part of chain (can be null)
|
|
* Returns:
|
|
* Throwable that is at the start of the chain; null if both `e1` and `e2` are null
|
|
*/
|
|
static @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2)
|
|
{
|
|
if (!e1)
|
|
return e2;
|
|
if (!e2)
|
|
return e1;
|
|
if (e2.refcount())
|
|
++e2.refcount();
|
|
|
|
for (auto e = e1; 1; e = e.nextInChain)
|
|
{
|
|
if (!e.nextInChain)
|
|
{
|
|
e.nextInChain = e2;
|
|
break;
|
|
}
|
|
}
|
|
return e1;
|
|
}
|
|
|
|
@nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
|
|
{
|
|
this.msg = msg;
|
|
this.nextInChain = nextInChain;
|
|
if (nextInChain && nextInChain._refcount)
|
|
++nextInChain._refcount;
|
|
//this.info = _d_traceContext();
|
|
}
|
|
|
|
@nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
|
|
{
|
|
this(msg, nextInChain);
|
|
this.file = file;
|
|
this.line = line;
|
|
//this.info = _d_traceContext();
|
|
}
|
|
|
|
@trusted nothrow ~this(){}
|
|
|
|
/**
|
|
* Overrides $(D Object.toString) and returns the error message.
|
|
* Internally this forwards to the $(D toString) overload that
|
|
* takes a $(D_PARAM sink) delegate.
|
|
*/
|
|
override string toString()
|
|
{
|
|
string s;
|
|
assert(false, "Not implemented for platform");
|
|
// toString((in buf) { s ~= buf; });
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* The Throwable hierarchy uses a toString overload that takes a
|
|
* $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be
|
|
* performed in certain error situations. Override this $(D
|
|
* toString) method to customize the error message.
|
|
*/
|
|
void toString(scope void delegate(in char[]) sink) const{}
|
|
|
|
/**
|
|
* Get the message describing the error.
|
|
* Base behavior is to return the `Throwable.msg` field.
|
|
* Override to return some other error message.
|
|
*
|
|
* Returns:
|
|
* Error message
|
|
*/
|
|
const(char)[] message() const
|
|
{
|
|
return this.msg;
|
|
}
|
|
}
|
|
|
|
class Exception : Throwable
|
|
{
|
|
|
|
/**
|
|
* Creates a new instance of Exception. The nextInChain parameter is used
|
|
* internally and should always be $(D null) when passed by user code.
|
|
* This constructor does not automatically throw the newly-created
|
|
* Exception; the $(D throw) statement should be used for that purpose.
|
|
*/
|
|
@nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
|
|
{
|
|
super(msg, file, line, nextInChain);
|
|
}
|
|
|
|
@nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
|
|
{
|
|
super(msg, file, line, nextInChain);
|
|
}
|
|
}
|
|
|
|
/*
|
|
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
|
|
{
|
|
TypeInfo ti = cast() cti;
|
|
while (ti)
|
|
{
|
|
// avoid dynamic type casts
|
|
auto tti = typeid(ti);
|
|
if (tti is typeid(TypeInfo_Const))
|
|
ti = (cast(TypeInfo_Const)cast(void*)ti).base;
|
|
else if (tti is typeid(TypeInfo_Invariant))
|
|
ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
|
|
else if (tti is typeid(TypeInfo_Shared))
|
|
ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
|
|
else if (tti is typeid(TypeInfo_Inout))
|
|
ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
|
|
else
|
|
break;
|
|
}
|
|
return ti;
|
|
}
|
|
*/
|
|
|
|
private void _doPostblit(T)(T[] arr)
|
|
{
|
|
// infer static postblit type, run postblit if any
|
|
static if (__traits(hasPostblit, T))
|
|
{
|
|
static if (__traits(isStaticArray, T) && is(T : E[], E))
|
|
_doPostblit(cast(E[]) arr);
|
|
else static if (!is(typeof(arr[0].__xpostblit())) && is(immutable T == immutable U, U))
|
|
foreach (ref elem; (() @trusted => cast(U[]) arr)())
|
|
elem.__xpostblit();
|
|
else
|
|
foreach (ref elem; arr)
|
|
elem.__xpostblit();
|
|
}
|
|
}
|
|
|
|
private inout(TypeInfo) getElement(return scope inout TypeInfo value) @trusted pure nothrow
|
|
{
|
|
TypeInfo element = cast() value;
|
|
for (;;)
|
|
{
|
|
if (auto qualified = cast(TypeInfo_Const) element)
|
|
element = qualified.base;
|
|
else if (auto redefined = cast(TypeInfo_Enum) element)
|
|
element = redefined.base;
|
|
else if (auto staticArray = cast(TypeInfo_StaticArray) element)
|
|
element = staticArray.value;
|
|
//else if (auto vector = cast(TypeInfo_Vector) element)
|
|
// element = vector.base;
|
|
else
|
|
break;
|
|
}
|
|
return cast(inout) element;
|
|
}
|
|
|
|
private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, const size_t count) @trusted nothrow
|
|
{
|
|
if (!count)
|
|
return 0;
|
|
|
|
const size_t elementSize = element.tsize;
|
|
if (!elementSize)
|
|
return 0;
|
|
|
|
static bool hasCustomToHash(const scope TypeInfo value) @trusted pure nothrow
|
|
{
|
|
const element = getElement(value);
|
|
|
|
if (const struct_ = cast(const TypeInfo_Struct) element)
|
|
return !!struct_.xtoHash;
|
|
|
|
return cast(const TypeInfo_Array) element
|
|
//|| cast(const TypeInfo_AssociativeArray) element
|
|
|| cast(const ClassInfo) element
|
|
|| cast(const TypeInfo_Interface) element;
|
|
}
|
|
|
|
if (!hasCustomToHash(element))
|
|
return hashOf(ptr[0 .. elementSize * count]);
|
|
|
|
size_t hash = 0;
|
|
foreach (size_t i; 0 .. count)
|
|
hash = hashOf(element.getHash(ptr + i * elementSize), hash);
|
|
return hash;
|
|
}
|
|
|
|
import core.internal.hash;
|