module DynamicCall; import tango.core.Traits; template isPointer(T) { const bool isPointer = isPointerType!(T); } enum ParamType { // No return value Void, // ST0 Float, Double, Real, // EAX Byte, Word, DWord, Pointer, // could probably be removed. Resolve to DWord/QWord instead // AArray, // isn't tested (merge with Pointer?) //EDX,EAX QWord, DArray, // isn't tested // Hidden pointer Hidden_Pointer, // for returning large (>8 bytes) structs, not tested yet } struct Param { ParamType type; // type of value void* ptr; // pointer to value } // This struct describes everything needed to make a call struct Call { Param[] input; // set of input arguments Param output; // result info void* funcptr; // function pointer } // makes a call, stores result in call.ouput void makeDynamicCall(Call* call) { switch (call.output.type) { case ParamType.Void: _makeCall!(void)(call); break; case ParamType.Pointer: makeCall!(void*)(call); break; case ParamType.Byte: makeCall!(byte)(call); break; case ParamType.Word: makeCall!(short)(call); break; case ParamType.DWord: makeCall!(int)(call); break; case ParamType.QWord: makeCall!(long)(call); break; case ParamType.Float: makeCall!(float)(call); break; case ParamType.Double: makeCall!(double)(call); break; case ParamType.Real: makeCall!(real)(call); break; } } // helper function to save some typing void makeCall(T)(Call* call) { *cast(T*)call.output.ptr = _makeCall!(T)(call); } T _makeCall(T)(Call* call) { void* funcptr = call.funcptr; void* argptr; int numArgs = call.input.length; if (numArgs != 0) { // this check is needed because last parameter is passed in EAX (if possible) Param* param = call.input.ptr; // iterate over first numArgs-1 arguments for ( ; --numArgs; ++param) { argptr = param.ptr; switch (param.type) { case ParamType.Byte: // push byte arg(*cast(byte*)argptr); break; case ParamType.Word: // push word arg(*cast(short*)argptr); break; case ParamType.Pointer: case ParamType.DWord: // push dword arg(*cast(int*)argptr); break; case ParamType.QWord: // push qword arg(*cast(long*)argptr); break; case ParamType.Float: // push float arg(*cast(float*)argptr); break; case ParamType.Double: // push double arg(*cast(double*)argptr); break; case ParamType.Real: // push real arg(*cast(real*)argptr); break; } } // same as above but passes in EAX if possible argptr = param.ptr; switch (param.type) { case ParamType.Byte: lastArg(*cast(byte*)argptr); break; case ParamType.Word: lastArg(*cast(short*)argptr); break; version(X86_64) { } else version (X86) { case ParamType.Pointer: } case ParamType.DWord: lastArg(*cast(int*)argptr); break; version(X86_64) { case ParamType.Pointer: } case ParamType.QWord: lastArg(*cast(long*)argptr); break; case ParamType.Float: lastArg(*cast(float*)argptr); break; case ParamType.Double: lastArg(*cast(double*)argptr); case ParamType.Real: lastArg(*cast(real*)argptr); } } asm { // call it! call funcptr; } } // A helper function that pushes an argument to stack in a type-safe manner // extern (System) is used so that argument isn't passed via EAX // does it work the same way in Linux? Or Linux uses __cdecl? // There must be other way to pass all the arguments on stack, but this one works well so far // Beautiful, isn't it? extern (System) void arg(T)(T arg) { asm { naked; ret; } } // A helper function that pushes an argument to stack in a type-safe manner // Allowed to pass argumet via EAX (that's why it's extern (D)) void lastArg(T)(T arg) { asm { naked; ret; } } // Convenient templates to map from type T to corresponding ParamType enum element template isStructSize(T, int size) { const int isStructSize = is (T == struct) && T.sizeof == size; } template ParamTypeFromT(T) { static if (is (T == byte) || is (T == ubyte) || is (T == char) || isStructSize!(T, 1)) { const ParamType ParamTypeFromT = ParamType.Byte; } else static if (is (T == short) || is (T == ushort) || is (T == wchar) || isStructSize!(T, 2)) { const ParamType ParamTypeFromT = ParamType.Word; } else static if (is (T == int) || is (T == uint) || is (T == dchar) || isStructSize!(T, 4)) { const ParamType ParamTypeFromT = ParamType.DWord; } else static if (is (T == long) || is (T == ulong) || isStructSize!(T, 8) || is (T == delegate)) { const ParamType ParamTypeFromT = ParamType.QWord; } else static if (is (T == float)) { const ParamType ParamTypeFromT = ParamType.Float; } else static if (is (T == double)) { const ParamType ParamTypeFromT = ParamType.Double; } else static if (is (T == real)) { const ParamType ParamTypeFromT = ParamType.Real; } else static if (is (T == void)) { const ParamType ParamTypeFromT = ParamType.Void; } else static if (isPointer!(T)) { const ParamType ParamTypeFromT = ParamType.Pointer; } }