mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-02-24 23:53:13 +01:00
1518 lines
38 KiB
D
1518 lines
38 KiB
D
/**
|
|
* Part of the D programming language runtime library.
|
|
* Forms the symbols available to all D programs. Includes
|
|
* Object, which is the root of the class object hierarchy.
|
|
*
|
|
* This module is implicitly imported.
|
|
* Macros:
|
|
* WIKI = Object
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2004-2007 by Digital Mars, www.digitalmars.com
|
|
* Written by Walter Bright
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, in both source and binary form, subject to the following
|
|
* restrictions:
|
|
*
|
|
* o The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* o Altered source versions must be plainly marked as such, and must not
|
|
* be misrepresented as being the original software.
|
|
* o This notice may not be removed or altered from any source
|
|
* distribution.
|
|
*/
|
|
|
|
/*
|
|
* Modified by Sean Kelly for use with the D Runtime Project
|
|
*/
|
|
|
|
module object;
|
|
|
|
private
|
|
{
|
|
import stdc.string;
|
|
import stdc.stdlib;
|
|
import util.string;
|
|
debug(PRINTF) import stdc.stdio;
|
|
|
|
extern (C) void onOutOfMemoryError();
|
|
extern (C) Object _d_newclass(ClassInfo ci);
|
|
}
|
|
|
|
// NOTE: For some reason, this declaration method doesn't work
|
|
// in this particular file (and this file only). It must
|
|
// be a DMD thing.
|
|
//alias typeof(int.sizeof) size_t;
|
|
//alias typeof(cast(void*)0 - cast(void*)0) ptrdiff_t;
|
|
|
|
version(X86_64)
|
|
{
|
|
alias ulong size_t;
|
|
alias long ptrdiff_t;
|
|
}
|
|
else
|
|
{
|
|
alias uint size_t;
|
|
alias int ptrdiff_t;
|
|
}
|
|
|
|
alias size_t hash_t;
|
|
alias bool equals_t;
|
|
|
|
alias invariant(char)[] string;
|
|
alias invariant(wchar)[] wstring;
|
|
alias invariant(dchar)[] dstring;
|
|
|
|
/**
|
|
* All D class objects inherit from Object.
|
|
*/
|
|
class Object
|
|
{
|
|
/**
|
|
* Convert Object to a human readable string.
|
|
*/
|
|
string toString()
|
|
{
|
|
return this.classinfo.name;
|
|
}
|
|
|
|
/**
|
|
* Compute hash function for Object.
|
|
*/
|
|
hash_t toHash()
|
|
{
|
|
// BUG: this prevents a compacting GC from working, needs to be fixed
|
|
return cast(hash_t)cast(void*)this;
|
|
}
|
|
|
|
/**
|
|
* Compare with another Object obj.
|
|
* Returns:
|
|
* $(TABLE
|
|
* $(TR $(TD this < obj) $(TD < 0))
|
|
* $(TR $(TD this == obj) $(TD 0))
|
|
* $(TR $(TD this > obj) $(TD > 0))
|
|
* )
|
|
*/
|
|
int opCmp(Object o)
|
|
{
|
|
// BUG: this prevents a compacting GC from working, needs to be fixed
|
|
//return cast(int)cast(void*)this - cast(int)cast(void*)o;
|
|
|
|
throw new Exception("need opCmp for class " ~ this.classinfo.name);
|
|
//return this !is o;
|
|
}
|
|
|
|
/**
|
|
* Returns !=0 if this object does have the same contents as obj.
|
|
*/
|
|
equals_t opEquals(Object o)
|
|
{
|
|
return this is o;
|
|
}
|
|
|
|
interface Monitor
|
|
{
|
|
void lock();
|
|
void unlock();
|
|
}
|
|
|
|
/**
|
|
* Create instance of class specified by classname.
|
|
* The class must either have no constructors or have
|
|
* a default constructor.
|
|
* Returns:
|
|
* null if failed
|
|
*/
|
|
static Object factory(string classname)
|
|
{
|
|
auto ci = ClassInfo.find(classname);
|
|
if (ci)
|
|
{
|
|
return ci.create();
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Information about an interface.
|
|
* When an object is accessed via an interface, an Interface* appears as the
|
|
* first entry in its vtbl.
|
|
*/
|
|
struct Interface
|
|
{
|
|
ClassInfo classinfo; /// .classinfo for this interface (not for containing class)
|
|
void*[] vtbl;
|
|
ptrdiff_t offset; /// offset to Interface 'this' from Object 'this'
|
|
}
|
|
|
|
/**
|
|
* Runtime type information about a class. Can be retrieved for any class type
|
|
* or instance by using the .classinfo property.
|
|
* A pointer to this appears as the first entry in the class's vtbl[].
|
|
*/
|
|
class ClassInfo : Object
|
|
{
|
|
byte[] 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
|
|
ClassInfo base; /// base class
|
|
void* destructor;
|
|
void function(Object) classInvariant;
|
|
uint flags;
|
|
// 1: // is IUnknown or is derived from IUnknown
|
|
// 2: // has no possible pointers into GC memory
|
|
// 4: // has offTi[] member
|
|
// 8: // has constructors
|
|
// 16: // has xgetMembers member
|
|
void* deallocator;
|
|
OffsetTypeInfo[] offTi;
|
|
void function(Object) defaultConstructor; // default Constructor
|
|
const(MemberInfo[]) function(in char[]) xgetMembers;
|
|
|
|
/**
|
|
* Search all modules for ClassInfo corresponding to classname.
|
|
* Returns: null if not found
|
|
*/
|
|
static ClassInfo find(in char[] classname)
|
|
{
|
|
foreach (m; ModuleInfo)
|
|
{
|
|
//writefln("module %s, %d", m.name, m.localClasses.length);
|
|
foreach (c; m.localClasses)
|
|
{
|
|
//writefln("\tclass %s", c.name);
|
|
if (c.name == classname)
|
|
return c;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Create instance of Object represented by 'this'.
|
|
*/
|
|
Object create()
|
|
{
|
|
if (flags & 8 && !defaultConstructor)
|
|
return null;
|
|
Object o = _d_newclass(this);
|
|
if (flags & 8 && defaultConstructor)
|
|
{
|
|
defaultConstructor(o);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
/**
|
|
* Search for all members with the name 'name'.
|
|
* If name[] is null, return all members.
|
|
*/
|
|
const(MemberInfo[]) getMembers(in char[] name)
|
|
{
|
|
if (flags & 16 && xgetMembers)
|
|
return xgetMembers(name);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
|
|
/**
|
|
* Runtime type information about a type.
|
|
* Can be retrieved for any type using a
|
|
* <a href="../expression.html#typeidexpression">TypeidExpression</a>.
|
|
*/
|
|
class TypeInfo
|
|
{
|
|
override hash_t toHash()
|
|
{
|
|
hash_t hash;
|
|
|
|
foreach (char c; this.toString())
|
|
hash = hash * 9 + c;
|
|
return hash;
|
|
}
|
|
|
|
override int opCmp(Object o)
|
|
{
|
|
if (this is o)
|
|
return 0;
|
|
TypeInfo ti = cast(TypeInfo)o;
|
|
if (ti is null)
|
|
return 1;
|
|
return dstrcmp(this.toString(), ti.toString());
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
/* TypeInfo instances are singletons, but duplicates can exist
|
|
* across DLL's. Therefore, comparing for a name match is
|
|
* sufficient.
|
|
*/
|
|
if (this is o)
|
|
return true;
|
|
TypeInfo ti = cast(TypeInfo)o;
|
|
return ti && this.toString() == ti.toString();
|
|
}
|
|
|
|
/// Returns a hash of the instance of a type.
|
|
hash_t getHash(in void* p) { return cast(hash_t)p; }
|
|
|
|
/// Compares two instances for equality.
|
|
equals_t equals(in void* p1, in void* p2) { return p1 == p2; }
|
|
|
|
/// Compares two instances for <, ==, or >.
|
|
int compare(in void* p1, in void* p2) { return 0; }
|
|
|
|
/// Returns size of the type.
|
|
size_t tsize() { return 0; }
|
|
|
|
/// Swaps two instances of the type.
|
|
void swap(void* p1, void* p2)
|
|
{
|
|
size_t n = tsize();
|
|
for (size_t i = 0; i < n; 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.
|
|
TypeInfo next() { return null; }
|
|
|
|
/// Return default initializer, null if default initialize to 0
|
|
void[] init() { return null; }
|
|
|
|
/// Get flags for type: 1 means GC should scan for pointers
|
|
uint flags() { return 0; }
|
|
|
|
/// Get type information on the contents of the type; null if not available
|
|
OffsetTypeInfo[] offTi() { return null; }
|
|
/// Run the destructor on the object and all its sub-objects
|
|
void destroy(void* p) {}
|
|
/// Run the postblit on the object and all its sub-objects
|
|
void postblit(void* p) {}
|
|
}
|
|
|
|
class TypeInfo_Typedef : TypeInfo
|
|
{
|
|
override string toString() { return name; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Typedef c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Typedef)o) !is null &&
|
|
this.name == c.name &&
|
|
this.base == c.base);
|
|
}
|
|
|
|
override hash_t getHash(in void* p) { return base.getHash(p); }
|
|
override equals_t equals(in void* p1, in void* p2) { return base.equals(p1, p2); }
|
|
override int compare(in void* p1, in void* p2) { return base.compare(p1, p2); }
|
|
override size_t tsize() { return base.tsize(); }
|
|
override void swap(void* p1, void* p2) { return base.swap(p1, p2); }
|
|
|
|
override TypeInfo next() { return base.next(); }
|
|
override uint flags() { return base.flags(); }
|
|
override void[] init() { return m_init.length ? m_init : base.init(); }
|
|
|
|
TypeInfo base;
|
|
string name;
|
|
void[] m_init;
|
|
}
|
|
|
|
class TypeInfo_Enum : TypeInfo_Typedef
|
|
{
|
|
|
|
}
|
|
|
|
class TypeInfo_Pointer : TypeInfo
|
|
{
|
|
override string toString() { return m_next.toString() ~ "*"; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Pointer c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Pointer)o) !is null &&
|
|
this.m_next == c.m_next);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
return cast(hash_t)*cast(void**)p;
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
return *cast(void**)p1 == *cast(void**)p2;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2)
|
|
{
|
|
if (*cast(void**)p1 < *cast(void**)p2)
|
|
return -1;
|
|
else if (*cast(void**)p1 > *cast(void**)p2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
override size_t tsize()
|
|
{
|
|
return (void*).sizeof;
|
|
}
|
|
|
|
override void swap(void* p1, void* p2)
|
|
{
|
|
void* tmp = *cast(void**)p1;
|
|
*cast(void**)p1 = *cast(void**)p2;
|
|
*cast(void**)p2 = tmp;
|
|
}
|
|
|
|
override TypeInfo next() { return m_next; }
|
|
override uint flags() { return 1; }
|
|
|
|
TypeInfo m_next;
|
|
}
|
|
|
|
class TypeInfo_Array : TypeInfo
|
|
{
|
|
override string toString() { return value.toString() ~ "[]"; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Array c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Array)o) !is null &&
|
|
this.value == c.value);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
size_t sz = value.tsize();
|
|
hash_t hash = 0;
|
|
void[] a = *cast(void[]*)p;
|
|
for (size_t i = 0; i < a.length; i++)
|
|
hash += value.getHash(a.ptr + i * sz);
|
|
return hash;
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
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)
|
|
{
|
|
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++)
|
|
{
|
|
int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz);
|
|
if (result)
|
|
return result;
|
|
}
|
|
return cast(int)a1.length - cast(int)a2.length;
|
|
}
|
|
|
|
override size_t tsize()
|
|
{
|
|
return (void[]).sizeof;
|
|
}
|
|
|
|
override void swap(void* p1, void* p2)
|
|
{
|
|
void[] tmp = *cast(void[]*)p1;
|
|
*cast(void[]*)p1 = *cast(void[]*)p2;
|
|
*cast(void[]*)p2 = tmp;
|
|
}
|
|
|
|
TypeInfo value;
|
|
|
|
override TypeInfo next()
|
|
{
|
|
return value;
|
|
}
|
|
|
|
override uint flags() { return 1; }
|
|
}
|
|
|
|
class TypeInfo_StaticArray : TypeInfo
|
|
{
|
|
override string toString()
|
|
{
|
|
char[10] tmp = void;
|
|
return cast(string)(value.toString() ~ "[" ~ tmp.intToString(len) ~ "]");
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_StaticArray c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_StaticArray)o) !is null &&
|
|
this.len == c.len &&
|
|
this.value == c.value);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
size_t sz = value.tsize();
|
|
hash_t hash = 0;
|
|
for (size_t i = 0; i < len; i++)
|
|
hash += value.getHash(p + i * sz);
|
|
return hash;
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
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)
|
|
{
|
|
size_t sz = value.tsize();
|
|
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
int result = value.compare(p1 + u * sz, p2 + u * sz);
|
|
if (result)
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
override size_t tsize()
|
|
{
|
|
return len * value.tsize();
|
|
}
|
|
|
|
override void swap(void* p1, void* p2)
|
|
{
|
|
void* tmp;
|
|
size_t sz = value.tsize();
|
|
ubyte[16] buffer;
|
|
void* pbuffer;
|
|
|
|
if (sz < buffer.sizeof)
|
|
tmp = buffer.ptr;
|
|
else
|
|
tmp = pbuffer = (new void[sz]).ptr;
|
|
|
|
for (size_t u = 0; u < len; u += sz)
|
|
{ size_t o = u * sz;
|
|
memcpy(tmp, p1 + o, sz);
|
|
memcpy(p1 + o, p2 + o, sz);
|
|
memcpy(p2 + o, tmp, sz);
|
|
}
|
|
if (pbuffer)
|
|
delete pbuffer;
|
|
}
|
|
|
|
override void[] init() { return value.init(); }
|
|
override TypeInfo next() { return value; }
|
|
override uint flags() { return value.flags(); }
|
|
|
|
override void destroy(void* p)
|
|
{
|
|
auto sz = value.tsize();
|
|
p += sz * len;
|
|
foreach (i; 0 .. len)
|
|
{
|
|
p -= sz;
|
|
value.destroy(p);
|
|
}
|
|
}
|
|
|
|
override void postblit(void* p)
|
|
{
|
|
auto sz = value.tsize();
|
|
foreach (i; 0 .. len)
|
|
{
|
|
value.postblit(p);
|
|
p += sz;
|
|
}
|
|
}
|
|
|
|
TypeInfo value;
|
|
size_t len;
|
|
}
|
|
|
|
class TypeInfo_AssociativeArray : TypeInfo
|
|
{
|
|
override string toString()
|
|
{
|
|
return cast(string)(next.toString() ~ "[" ~ key.toString() ~ "]");
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_AssociativeArray c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_AssociativeArray)o) !is null &&
|
|
this.key == c.key &&
|
|
this.value == c.value);
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override size_t tsize()
|
|
{
|
|
return (char[int]).sizeof;
|
|
}
|
|
|
|
override TypeInfo next() { return value; }
|
|
override uint flags() { return 1; }
|
|
|
|
TypeInfo value;
|
|
TypeInfo key;
|
|
}
|
|
|
|
class TypeInfo_Function : TypeInfo
|
|
{
|
|
override string toString()
|
|
{
|
|
return cast(string)(next.toString() ~ "()");
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Function c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Function)o) !is null &&
|
|
this.next == c.next);
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override size_t tsize()
|
|
{
|
|
return 0; // no size for functions
|
|
}
|
|
|
|
TypeInfo next;
|
|
}
|
|
|
|
class TypeInfo_Delegate : TypeInfo
|
|
{
|
|
override string toString()
|
|
{
|
|
return cast(string)(next.toString() ~ " delegate()");
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Delegate c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Delegate)o) !is null &&
|
|
this.next == c.next);
|
|
}
|
|
|
|
// BUG: need to add the rest of the functions
|
|
|
|
override size_t tsize()
|
|
{
|
|
alias int delegate() dg;
|
|
return dg.sizeof;
|
|
}
|
|
|
|
override uint flags() { return 1; }
|
|
|
|
TypeInfo next;
|
|
}
|
|
|
|
class TypeInfo_Class : TypeInfo
|
|
{
|
|
override string toString() { return info.name; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Class c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Class)o) !is null &&
|
|
this.info.name == c.classinfo.name);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
Object o = *cast(Object*)p;
|
|
return o ? o.toHash() : 0;
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
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)
|
|
{
|
|
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 size_t tsize()
|
|
{
|
|
return Object.sizeof;
|
|
}
|
|
|
|
override uint flags() { return 1; }
|
|
|
|
override OffsetTypeInfo[] offTi()
|
|
{
|
|
return (info.flags & 4) ? info.offTi : null;
|
|
}
|
|
|
|
ClassInfo info;
|
|
}
|
|
|
|
class TypeInfo_Interface : TypeInfo
|
|
{
|
|
override string toString() { return info.name; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Interface c;
|
|
return this is o ||
|
|
((c = cast(TypeInfo_Interface)o) !is null &&
|
|
this.info.name == c.classinfo.name);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
Interface* pi = **cast(Interface ***)*cast(void**)p;
|
|
Object o = cast(Object)(*cast(void**)p - pi.offset);
|
|
assert(o);
|
|
return o.toHash();
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
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)
|
|
{
|
|
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 size_t tsize()
|
|
{
|
|
return Object.sizeof;
|
|
}
|
|
|
|
override uint flags() { return 1; }
|
|
|
|
ClassInfo info;
|
|
}
|
|
|
|
class TypeInfo_Struct : TypeInfo
|
|
{
|
|
override string toString() { return name; }
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
TypeInfo_Struct s;
|
|
return this is o ||
|
|
((s = cast(TypeInfo_Struct)o) !is null &&
|
|
this.name == s.name &&
|
|
this.init.length == s.init.length);
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
assert(p);
|
|
if (xtoHash)
|
|
{
|
|
debug(PRINTF) printf("getHash() using xtoHash\n");
|
|
return (*xtoHash)(p);
|
|
}
|
|
else
|
|
{
|
|
hash_t h;
|
|
debug(PRINTF) printf("getHash() using default hash\n");
|
|
// A sorry hash algorithm.
|
|
// Should use the one for strings.
|
|
// BUG: relies on the GC not moving objects
|
|
auto q = cast(const(ubyte)*)p;
|
|
for (size_t i = 0; i < init.length; i++)
|
|
{
|
|
h = h * 9 + *q;
|
|
q++;
|
|
}
|
|
return h;
|
|
}
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
if (p1 == p2)
|
|
return true;
|
|
else if (!p1 || !p2)
|
|
return false;
|
|
else if (xopEquals)
|
|
return (*xopEquals)(p1, p2);
|
|
else
|
|
// BUG: relies on the GC not moving objects
|
|
return memcmp(p1, p2, init.length) == 0;
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2)
|
|
{
|
|
// Regard null references as always being "less than"
|
|
if (p1 != p2)
|
|
{
|
|
if (p1)
|
|
{
|
|
if (!p2)
|
|
return true;
|
|
else if (xopCmp)
|
|
return (*xopCmp)(p2, p1);
|
|
else
|
|
// BUG: relies on the GC not moving objects
|
|
return memcmp(p1, p2, init.length);
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
override size_t tsize()
|
|
{
|
|
return init.length;
|
|
}
|
|
|
|
override void[] init() { return m_init; }
|
|
|
|
override uint flags() { return m_flags; }
|
|
|
|
override void destroy(void* p)
|
|
{
|
|
if (xdtor)
|
|
(*xdtor)(p);
|
|
}
|
|
|
|
override void postblit(void* p)
|
|
{
|
|
if (xpostblit)
|
|
(*xpostblit)(p);
|
|
}
|
|
|
|
string name;
|
|
void[] m_init; // initializer; init.ptr == null if 0 initialize
|
|
|
|
hash_t function(in void*) xtoHash;
|
|
equals_t function(in void*, in void*) xopEquals;
|
|
int function(in void*, in void*) xopCmp;
|
|
char[] function(in void*) xtoString;
|
|
|
|
uint m_flags;
|
|
|
|
const(MemberInfo[]) function(in char[]) xgetMembers;
|
|
void function(void*) xdtor;
|
|
void function(void*) xpostblit;
|
|
}
|
|
|
|
class TypeInfo_Tuple : TypeInfo
|
|
{
|
|
TypeInfo[] elements;
|
|
|
|
override string toString()
|
|
{
|
|
string s = "(";
|
|
foreach (i, element; elements)
|
|
{
|
|
if (i)
|
|
s ~= ',';
|
|
s ~= element.toString();
|
|
}
|
|
s ~= ")";
|
|
return s;
|
|
}
|
|
|
|
override equals_t opEquals(Object o)
|
|
{
|
|
if (this is o)
|
|
return true;
|
|
|
|
auto t = cast(TypeInfo_Tuple)o;
|
|
if (t && elements.length == t.elements.length)
|
|
{
|
|
for (size_t i = 0; i < elements.length; i++)
|
|
{
|
|
if (elements[i] != t.elements[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override hash_t getHash(in void* p)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override equals_t equals(in void* p1, in void* p2)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override int compare(in void* p1, in void* p2)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override size_t tsize()
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override void swap(void* p1, void* p2)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override void destroy(void* p)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override void postblit(void* p)
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
class TypeInfo_Const : TypeInfo
|
|
{
|
|
override string toString()
|
|
{
|
|
return cast(string) ("const(" ~ base.toString() ~ ")");
|
|
}
|
|
|
|
override equals_t opEquals(Object o) { return base.opEquals(o); }
|
|
override hash_t getHash(in void *p) { return base.getHash(p); }
|
|
override equals_t equals(in void *p1, in void *p2) { return base.equals(p1, p2); }
|
|
override int compare(in void *p1, in void *p2) { return base.compare(p1, p2); }
|
|
override size_t tsize() { return base.tsize(); }
|
|
override void swap(void *p1, void *p2) { return base.swap(p1, p2); }
|
|
|
|
override TypeInfo next() { return base.next(); }
|
|
override uint flags() { return base.flags(); }
|
|
override void[] init() { return base.init(); }
|
|
|
|
TypeInfo base;
|
|
}
|
|
|
|
class TypeInfo_Invariant : TypeInfo_Const
|
|
{
|
|
override string toString()
|
|
{
|
|
return cast(string) ("invariant(" ~ base.toString() ~ ")");
|
|
}
|
|
}
|
|
|
|
abstract class MemberInfo
|
|
{
|
|
string name();
|
|
}
|
|
|
|
class MemberInfo_field : MemberInfo
|
|
{
|
|
this(string name, TypeInfo ti, size_t offset)
|
|
{
|
|
m_name = name;
|
|
m_typeinfo = ti;
|
|
m_offset = offset;
|
|
}
|
|
|
|
override string name() { return m_name; }
|
|
TypeInfo typeInfo() { return m_typeinfo; }
|
|
size_t offset() { return m_offset; }
|
|
|
|
string m_name;
|
|
TypeInfo m_typeinfo;
|
|
size_t m_offset;
|
|
}
|
|
|
|
class MemberInfo_function : MemberInfo
|
|
{
|
|
this(string name, TypeInfo ti, void* fp, uint flags)
|
|
{
|
|
m_name = name;
|
|
m_typeinfo = ti;
|
|
m_fp = fp;
|
|
m_flags = flags;
|
|
}
|
|
|
|
override string name() { return m_name; }
|
|
TypeInfo typeInfo() { return m_typeinfo; }
|
|
void* fp() { return m_fp; }
|
|
uint flags() { return m_flags; }
|
|
|
|
string m_name;
|
|
TypeInfo m_typeinfo;
|
|
void* m_fp;
|
|
uint m_flags;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Throwable
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
class Throwable : Object
|
|
{
|
|
interface TraceInfo
|
|
{
|
|
int opApply(int delegate(inout char[]));
|
|
}
|
|
|
|
string msg;
|
|
string file;
|
|
size_t line;
|
|
TraceInfo info;
|
|
Throwable next;
|
|
|
|
this(string msg, Throwable next = null)
|
|
{
|
|
this.msg = msg;
|
|
this.next = next;
|
|
this.info = traceContext();
|
|
}
|
|
|
|
this(string msg, string file, size_t line, Throwable next = null)
|
|
{
|
|
this(msg, next);
|
|
this.file = file;
|
|
this.line = line;
|
|
this.info = traceContext();
|
|
}
|
|
|
|
override string toString()
|
|
{
|
|
char[10] tmp = void;
|
|
char[] buf;
|
|
|
|
for (Throwable e = this; e !is null; e = e.next)
|
|
{
|
|
if (e.file)
|
|
{
|
|
buf ~= e.classinfo.name ~ "@" ~ e.file ~ "(" ~ tmp.intToString(e.line) ~ "): " ~ e.msg;
|
|
}
|
|
else
|
|
{
|
|
buf ~= e.classinfo.name ~ ": " ~ e.msg;
|
|
}
|
|
if (e.info)
|
|
{
|
|
buf ~= "\n----------------";
|
|
foreach (t; e.info)
|
|
buf ~= "\n" ~ t;
|
|
}
|
|
if (e.next)
|
|
buf ~= "\n";
|
|
}
|
|
return cast(string) buf;
|
|
}
|
|
}
|
|
|
|
|
|
alias Throwable.TraceInfo function(void* ptr = null) TraceHandler;
|
|
private TraceHandler traceHandler = null;
|
|
|
|
|
|
/**
|
|
* Overrides the default trace hander with a user-supplied version.
|
|
*
|
|
* Params:
|
|
* h = The new trace handler. Set to null to use the default handler.
|
|
*/
|
|
extern (C) void rt_setTraceHandler(TraceHandler h)
|
|
{
|
|
traceHandler = h;
|
|
}
|
|
|
|
|
|
/**
|
|
* This function will be called when an exception is constructed. The
|
|
* user-supplied trace handler will be called if one has been supplied,
|
|
* otherwise no trace will be generated.
|
|
*
|
|
* Params:
|
|
* ptr = A pointer to the location from which to generate the trace, or null
|
|
* if the trace should be generated from within the trace handler
|
|
* itself.
|
|
*
|
|
* Returns:
|
|
* An object describing the current calling context or null if no handler is
|
|
* supplied.
|
|
*/
|
|
Throwable.TraceInfo traceContext(void* ptr = null)
|
|
{
|
|
if (traceHandler is null)
|
|
return null;
|
|
return traceHandler(ptr);
|
|
}
|
|
|
|
|
|
class Exception : Throwable
|
|
{
|
|
this(string msg, Throwable next = null)
|
|
{
|
|
super(msg, next);
|
|
}
|
|
|
|
this(string msg, string file, size_t line, Throwable next = null)
|
|
{
|
|
super(msg, file, line, next);
|
|
}
|
|
}
|
|
|
|
|
|
class Error : Throwable
|
|
{
|
|
this(string msg, Throwable next = null)
|
|
{
|
|
super(msg, next);
|
|
}
|
|
|
|
this(string msg, string file, size_t line, Throwable next = null)
|
|
{
|
|
super(msg, file, line, next);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ModuleInfo
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
enum
|
|
{
|
|
MIctorstart = 1, // we've started constructing it
|
|
MIctordone = 2, // finished construction
|
|
MIstandalone = 4, // module ctor does not depend on other module
|
|
// ctors being done first
|
|
MIhasictor = 8, // has ictor member
|
|
}
|
|
|
|
|
|
class ModuleInfo
|
|
{
|
|
string name;
|
|
ModuleInfo[] importedModules;
|
|
ClassInfo[] localClasses;
|
|
uint flags;
|
|
|
|
void function() ctor; // module static constructor (order dependent)
|
|
void function() dtor; // module static destructor
|
|
void function() unitTest; // module unit tests
|
|
|
|
void* xgetMembers; // module getMembers() function
|
|
|
|
void function() ictor; // module static constructor (order independent)
|
|
|
|
static int opApply(int delegate(inout ModuleInfo) dg)
|
|
{
|
|
int ret = 0;
|
|
|
|
foreach (m; _moduleinfo_array)
|
|
{
|
|
ret = dg(m);
|
|
if (ret)
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
// Windows: this gets initialized by minit.asm
|
|
// linux: this gets initialized in _moduleCtor()
|
|
extern (C) ModuleInfo[] _moduleinfo_array;
|
|
|
|
|
|
version (linux)
|
|
{
|
|
// This linked list is created by a compiler generated function inserted
|
|
// into the .ctor list by the compiler.
|
|
struct ModuleReference
|
|
{
|
|
ModuleReference* next;
|
|
ModuleInfo mod;
|
|
}
|
|
|
|
extern (C) ModuleReference* _Dmodule_ref; // start of linked list
|
|
}
|
|
|
|
ModuleInfo[] _moduleinfo_dtors;
|
|
uint _moduleinfo_dtors_i;
|
|
|
|
// Register termination function pointers
|
|
extern (C) int _fatexit(void*);
|
|
|
|
/**
|
|
* Initialize the modules.
|
|
*/
|
|
|
|
extern (C) void _moduleCtor()
|
|
{
|
|
debug(PRINTF) printf("_moduleCtor()\n");
|
|
version (linux)
|
|
{
|
|
int len = 0;
|
|
ModuleReference *mr;
|
|
|
|
for (mr = _Dmodule_ref; mr; mr = mr.next)
|
|
len++;
|
|
_moduleinfo_array = new ModuleInfo[len];
|
|
len = 0;
|
|
for (mr = _Dmodule_ref; mr; mr = mr.next)
|
|
{ _moduleinfo_array[len] = mr.mod;
|
|
len++;
|
|
}
|
|
}
|
|
|
|
version (Windows)
|
|
{
|
|
// Ensure module destructors also get called on program termination
|
|
//_fatexit(&_STD_moduleDtor);
|
|
}
|
|
|
|
_moduleinfo_dtors = new ModuleInfo[_moduleinfo_array.length];
|
|
debug(PRINTF) printf("_moduleinfo_dtors = x%x\n", cast(void*)_moduleinfo_dtors);
|
|
_moduleIndependentCtors();
|
|
_moduleCtor2(_moduleinfo_array, 0);
|
|
}
|
|
|
|
extern (C) void _moduleIndependentCtors()
|
|
{
|
|
debug(PRINTF) printf("_moduleIndependentCtors()\n");
|
|
foreach (m; _moduleinfo_array)
|
|
{
|
|
if (m && m.flags & MIhasictor && m.ictor)
|
|
{
|
|
(*m.ictor)();
|
|
}
|
|
}
|
|
}
|
|
|
|
void _moduleCtor2(ModuleInfo[] mi, int skip)
|
|
{
|
|
debug(PRINTF) printf("_moduleCtor2(): %d modules\n", mi.length);
|
|
for (uint i = 0; i < mi.length; i++)
|
|
{
|
|
ModuleInfo m = mi[i];
|
|
|
|
debug(PRINTF) printf("\tmodule[%d] = '%p'\n", i, m);
|
|
if (!m)
|
|
continue;
|
|
debug(PRINTF) printf("\tmodule[%d] = '%.*s'\n", i, m.name);
|
|
if (m.flags & MIctordone)
|
|
continue;
|
|
debug(PRINTF) printf("\tmodule[%d] = '%.*s', m = x%x\n", i, m.name, m);
|
|
|
|
if (m.ctor || m.dtor)
|
|
{
|
|
if (m.flags & MIctorstart)
|
|
{ if (skip || m.flags & MIstandalone)
|
|
continue;
|
|
throw new Exception("Cyclic dependency in module " ~ m.name);
|
|
}
|
|
|
|
m.flags |= MIctorstart;
|
|
_moduleCtor2(m.importedModules, 0);
|
|
if (m.ctor)
|
|
(*m.ctor)();
|
|
m.flags &= ~MIctorstart;
|
|
m.flags |= MIctordone;
|
|
|
|
// Now that construction is done, register the destructor
|
|
//printf("\tadding module dtor x%x\n", m);
|
|
assert(_moduleinfo_dtors_i < _moduleinfo_dtors.length);
|
|
_moduleinfo_dtors[_moduleinfo_dtors_i++] = m;
|
|
}
|
|
else
|
|
{
|
|
m.flags |= MIctordone;
|
|
_moduleCtor2(m.importedModules, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destruct the modules.
|
|
*/
|
|
|
|
// Starting the name with "_STD" means under linux a pointer to the
|
|
// function gets put in the .dtors segment.
|
|
|
|
extern (C) void _moduleDtor()
|
|
{
|
|
debug(PRINTF) printf("_moduleDtor(): %d modules\n", _moduleinfo_dtors_i);
|
|
|
|
for (uint i = _moduleinfo_dtors_i; i-- != 0;)
|
|
{
|
|
ModuleInfo m = _moduleinfo_dtors[i];
|
|
|
|
debug(PRINTF) printf("\tmodule[%d] = '%.*s', x%x\n", i, m.name, m);
|
|
if (m.dtor)
|
|
{
|
|
(*m.dtor)();
|
|
}
|
|
}
|
|
debug(PRINTF) printf("_moduleDtor() done\n");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Monitor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
alias Object.Monitor IMonitor;
|
|
alias void delegate(Object) DEvent;
|
|
|
|
// NOTE: The dtor callback feature is only supported for monitors that are not
|
|
// supplied by the user. The assumption is that any object with a user-
|
|
// supplied monitor may have special storage or lifetime requirements and
|
|
// that as a result, storing references to local objects within Monitor
|
|
// may not be safe or desirable. Thus, devt is only valid if impl is
|
|
// null.
|
|
struct Monitor
|
|
{
|
|
IMonitor impl;
|
|
/* internal */
|
|
DEvent[] devt;
|
|
/* stuff */
|
|
}
|
|
|
|
Monitor* getMonitor(Object h)
|
|
{
|
|
return cast(Monitor*) (cast(void**) h)[1];
|
|
}
|
|
|
|
void setMonitor(Object h, Monitor* m)
|
|
{
|
|
(cast(void**) h)[1] = m;
|
|
}
|
|
|
|
extern (C) void _d_monitor_create(Object);
|
|
extern (C) void _d_monitor_destroy(Object);
|
|
extern (C) void _d_monitor_lock(Object);
|
|
extern (C) int _d_monitor_unlock(Object);
|
|
|
|
extern (C) void _d_monitordelete(Object h, bool det)
|
|
{
|
|
Monitor* m = getMonitor(h);
|
|
|
|
if (m !is null)
|
|
{
|
|
IMonitor i = m.impl;
|
|
if (i is null)
|
|
{
|
|
_d_monitor_devt(m, h);
|
|
_d_monitor_destroy(h);
|
|
setMonitor(h, null);
|
|
return;
|
|
}
|
|
if (det && (cast(void*) i) !is (cast(void*) h))
|
|
delete i;
|
|
setMonitor(h, null);
|
|
}
|
|
}
|
|
|
|
extern (C) void _d_monitorenter(Object h)
|
|
{
|
|
Monitor* m = getMonitor(h);
|
|
|
|
if (m is null)
|
|
{
|
|
_d_monitor_create(h);
|
|
m = getMonitor(h);
|
|
}
|
|
|
|
IMonitor i = m.impl;
|
|
|
|
if (i is null)
|
|
{
|
|
_d_monitor_lock(h);
|
|
return;
|
|
}
|
|
i.lock();
|
|
}
|
|
|
|
extern (C) void _d_monitorexit(Object h)
|
|
{
|
|
Monitor* m = getMonitor(h);
|
|
IMonitor i = m.impl;
|
|
|
|
if (i is null)
|
|
{
|
|
_d_monitor_unlock(h);
|
|
return;
|
|
}
|
|
i.unlock();
|
|
}
|
|
|
|
extern (C) void _d_monitor_devt(Monitor* m, Object h)
|
|
{
|
|
if (m.devt.length)
|
|
{
|
|
DEvent[] devt;
|
|
|
|
synchronized (h)
|
|
{
|
|
devt = m.devt;
|
|
m.devt = null;
|
|
}
|
|
foreach (v; devt)
|
|
{
|
|
if (v)
|
|
v(h);
|
|
}
|
|
free(devt.ptr);
|
|
}
|
|
}
|
|
|
|
extern (C) void rt_attachDisposeEvent(Object h, DEvent e)
|
|
{
|
|
synchronized (h)
|
|
{
|
|
Monitor* m = getMonitor(h);
|
|
assert(m.impl is null);
|
|
|
|
foreach (inout v; m.devt)
|
|
{
|
|
if (v is null || v == e)
|
|
{
|
|
v = e;
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto len = m.devt.length + 4; // grow by 4 elements
|
|
auto pos = m.devt.length; // insert position
|
|
auto p = realloc(m.devt.ptr, DEvent.sizeof * len);
|
|
if (!p)
|
|
onOutOfMemoryError();
|
|
m.devt = (cast(DEvent*)p)[0 .. len];
|
|
m.devt[pos+1 .. len] = null;
|
|
m.devt[pos] = e;
|
|
}
|
|
}
|
|
|
|
extern (C) void rt_detachDisposeEvent(Object h, DEvent e)
|
|
{
|
|
synchronized (h)
|
|
{
|
|
Monitor* m = getMonitor(h);
|
|
assert(m.impl is null);
|
|
|
|
foreach (p, v; m.devt)
|
|
{
|
|
if (v == e)
|
|
{
|
|
memmove(&m.devt[p],
|
|
&m.devt[p+1],
|
|
(m.devt.length - p - 1) * DEvent.sizeof);
|
|
m.devt[$ - 1] = null;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|