ref: 7d02e382d314d5bdde7978ccb7a64ea9201d03db
dir: /ficlplatform/win32.c/
/*
** win32.c
** submitted to Ficl by Larry Hastings, larry@hastings.org
**/
#include <sys/stat.h>
#include "ficl.h"
/*
**
** Heavy, undocumented wizardry here.
**
** In Win32, like most OSes, the buffered file I/O functions in the
** C API (functions that take a FILE * like fopen()) are implemented
** on top of the raw file I/O functions (functions that take an int,
** like open()). However, in Win32, these functions in turn are
** implemented on top of the Win32 native file I/O functions (functions
** that take a HANDLE, like CreateFile()). This behavior is undocumented
** but easy to deduce by reading the CRT/SRC directory.
**
** The below mishmash of typedefs and defines were copied from
** CRT/SRC/INTERNAL.H from MSVC.
**
** --lch
*/
typedef struct {
long osfhnd; /* underlying OS file HANDLE */
char osfile; /* attributes of file (e.g., open in text mode?) */
char pipech; /* one char buffer for handles opened on pipes */
#ifdef _MT
int lockinitflag;
CRITICAL_SECTION lock;
#endif /* _MT */
} ioinfo;
extern _CRTIMP ioinfo * __pioinfo[];
#define IOINFO_L2E 5
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
#define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - \
1)) )
#define _osfhnd(i) ( _pioinfo(i)->osfhnd )
int ficlFileTruncate(ficlFile *ff, ficlUnsigned size)
{
HANDLE hFile = (HANDLE)_osfhnd(_fileno(ff->f));
if (SetFilePointer(hFile, size, NULL, FILE_BEGIN) != size)
return 0;
return !SetEndOfFile(hFile);
}
int ficlFileStatus(char *filename, int *status)
{
/*
** The Windows documentation for GetFileAttributes() says it returns
** INVALID_FILE_ATTRIBUTES on error. There's no such #define. The
** return value for error is -1, so we'll just use that.
*/
DWORD attributes = GetFileAttributes(filename);
if (attributes == -1)
{
*status = GetLastError();
return -1;
}
*status = attributes;
return 0;
}
long ficlFileSize(ficlFile *ff)
{
struct stat statbuf;
if (ff == NULL)
return -1;
statbuf.st_size = -1;
if (fstat(fileno(ff->f), &statbuf) != 0)
return -1;
return statbuf.st_size;
}
void *ficlMalloc(size_t size)
{
return malloc(size);
}
void *ficlRealloc(void *p, size_t size)
{
return realloc(p, size);
}
void ficlFree(void *p)
{
free(p);
}
void ficlCallbackDefaultTextOut(ficlCallback *callback, char *message)
{
FICL_IGNORE(callback);
if (message != NULL)
fputs(message, stdout);
else
fflush(stdout);
return;
}
/*
**
** Platform-specific functions
**
*/
/*
** m u l t i c a l l
**
** The be-all, end-all, swiss-army-chainsaw of native function call methods in Ficl.
**
** Usage:
** ( x*argumentCount [this] [vtable] argumentCount floatArgumentBitfield cstringArgumentBitfield functionAddress flags -- returnValue | )
** Note that any/all of the arguments (x*argumentCount) and the return value can use the
** float stack instead of the data stack.
**
** To call a simple native function:
** call with flags = MULTICALL_CALLTYPE_FUNCTION
** To call a method on an object:
** pass in the "this" pointer just below argumentCount,
** call with flags = MULTICALL_CALLTYPE_METHOD
** *do not* include the "this" pointer for the purposes of argumentCount
** To call a virtual method on an object:
** pass in the "this" pointer just below argumentCount,
** call with flags = MULTICALL_CALLTYPE_VIRTUAL_METHOD
** *do not* include the "this" pointer for the purposes of argumentCount
** the function address must be the offset into the vtable for that function
** It doesn't matter whether the function you're calling is "stdcall" (caller pops
** the stack) or "fastcall" (callee pops the stack); for robustness, multicall
** always restores the original stack pointer anyway.
**
**
** To handle floating-point arguments:
** To thunk an argument from the float stack instead of the data stack, set the corresponding bit
** in the "floatArgumentBitfield" argument. Argument zero is bit 0 (1), argument one is bit 1 (2),
** argument 2 is is bit 2 (4), argument 3 is bit 3 (8), etc. For instance, to call this function:
** float greasyFingers(int a, float b, int c, float d)
** you would call
** 4 \ argumentCount
** 2 8 or \ floatArgumentBitfield, thunk argument 2 (2) and 4 (8)
** 0 \ cstringArgumentBitfield, don't thunk any arguments
** (addressOfGreasyFingers) MULTICALL-CALLTYPE-FUNCTION MULTICALL-RETURNTYPE-FLOAT or multicall
**
** To handle automatic conversion of addr-u arguments to C-style strings:
** This is much like handling float arguments. The bit set in cstringArgumentBitfield specifies
** the *length* argument (the higher of the two arguments) for each addr-u you want converted.
** You must count *both* arguments for the purposes of the argumentCount parameter.
** For instance, to call the Win32 function MessageBoxA:
**
** 0 "Howdy there!" "Title" 0
** 6 \ argument count is 6! flags text-addr text-u title-addr title-u hwnd
** 0 \ floatArgumentBitfield, don't thunk any float arguments
** 2 8 or \ cstringArgumentBitfield, thunk for title-u (argument 2, 2) and text-u (argument 4, 8)
** (addressOfMessageBoxA) MULTICALL-CALLTYPE-FUNCTION MULTICALL-RETURNTYPE-INTEGER or multicall
** The strings are copied to temporary storage and appended with a zero. These strings are freed
** before multicall returns. If you need to call functions that write to these string buffers,
** you'll need to handle thunking those arguments yourself.
**
** (If you want to call a function with more than 32 parameters, and do thunking, you need to hit somebody
** in the head with a rock. Note: this could be you!)
**
** Note that, big surprise, this function is really really really dependent
** on predefined behavior of Win32 and MSVC. It would be non-zero amounts of
** work to port to Win64, Linux, other compilers, etc.
**
** --lch
*/
static void ficlPrimitiveMulticall(ficlVm *vm)
{
int flags;
int functionAddress;
int argumentCount;
int *thisPointer;
int integerReturnValue;
#if FICL_WANT_FLOAT
float floatReturnValue;
#endif /* FICL_WANT_FLOAT */
int cstringArguments;
int floatArguments;
int i;
char **fixups;
int fixupCount;
int fixupIndex;
int *argumentPointer;
int finalArgumentCount;
int argumentDirection;
int *adjustedArgumentPointer;
int originalESP;
int vtable;
flags = ficlStackPopInteger(vm->dataStack);
functionAddress = ficlStackPopInteger(vm->dataStack);
if (FICL_MULTICALL_GET_CALLTYPE(flags) == FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD)
functionAddress *= 4;
cstringArguments = ficlStackPopInteger(vm->dataStack);
floatArguments = ficlStackPopInteger(vm->dataStack);
#if !FICL_WANT_FLOAT
FICL_VM_ASSERT(vm, !floatArguments);
FICL_VM_ASSERT(vm, FICL_MULTICALL_GET_RETURNTYPE(flags) != FICL_MULTICALL_RETURNTYPE_FLOAT);
#endif /* !FICL_WANT_FLOAT */
argumentCount = ficlStackPopInteger(vm->dataStack);
fixupCount = 0;
if (cstringArguments)
{
for (i = 0; i < argumentCount; i++)
if (cstringArguments & (1 << i))
fixupCount++;
fixups = (char **)malloc(fixupCount * sizeof(char *));
}
else
{
fixups = NULL;
}
/* argumentCount does *not* include the *this* pointer! */
if (FICL_MULTICALL_GET_CALLTYPE(flags) != FICL_MULTICALL_CALLTYPE_FUNCTION)
{
if (flags & FICL_MULTICALL_EXPLICIT_VTABLE)
vtable = ficlStackPopInteger(vm->dataStack);
__asm push ecx
thisPointer = (int *)ficlStackPopPointer(vm->dataStack);
if ((flags & FICL_MULTICALL_EXPLICIT_VTABLE) == 0)
vtable = *thisPointer;
}
__asm mov originalESP, esp
fixupIndex = 0;
finalArgumentCount = argumentCount - fixupCount;
__asm mov argumentPointer, esp
adjustedArgumentPointer = argumentPointer - finalArgumentCount;
__asm mov esp, adjustedArgumentPointer
if (flags & FICL_MULTICALL_REVERSE_ARGUMENTS)
{
argumentDirection = -1;
argumentPointer--;
}
else
{
argumentPointer = adjustedArgumentPointer;
argumentDirection = 1;
}
for (i = 0; i < argumentCount; i++)
{
int argument;
/* a single argument can't be both a float and a cstring! */
FICL_VM_ASSERT(vm, !((floatArguments & 1) && (cstringArguments & 1)));
#if FICL_WANT_FLOAT
if (floatArguments & 1)
argument = ficlStackPopInteger(vm->floatStack);
else
#endif /* FICL_WANT_FLOAT */
argument = ficlStackPopInteger(vm->dataStack);
if (cstringArguments & 1)
{
int length;
char *address;
char *buffer;
address = ficlStackPopPointer(vm->dataStack);
length = argument;
buffer = malloc(length + 1);
memcpy(buffer, address, length);
buffer[length] = 0;
fixups[fixupIndex++] = buffer;
argument = (int)buffer;
argumentCount--;
floatArguments >>= 1;
cstringArguments >>= 1;
}
*argumentPointer = argument;
argumentPointer += argumentDirection;
floatArguments >>= 1;
cstringArguments >>= 1;
}
/*
** note! leave the "mov ecx, thisPointer" code where it is.
** yes, it's duplicated in two spots.
** however, MSVC likes to use ecx as a scratch variable,
** so we want to set it as close as possible before the call.
*/
if (FICL_MULTICALL_GET_CALLTYPE(flags) == FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD)
{
__asm
{
/* push thisPointer */
mov ecx, thisPointer
/* put vtable into eax. */
mov eax, vtable
/* pull out the address of the function we want... */
add eax, functionAddress
/* and call it. */
call [eax]
}
}
else
{
FICL_VM_ASSERT(vm, functionAddress != 0);
if (FICL_MULTICALL_GET_CALLTYPE(flags))
{
__asm mov ecx, thisPointer
}
__asm call functionAddress
}
/* save off the return value, if there is one */
__asm mov integerReturnValue, eax
#if FICL_WANT_FLOAT
__asm fst floatReturnValue
#endif /* FICL_WANT_FLOAT */
__asm mov esp, originalESP
if (FICL_MULTICALL_GET_CALLTYPE(flags))
{
__asm pop ecx
}
if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_INTEGER)
ficlStackPushInteger(vm->dataStack, integerReturnValue);
else if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_CSTRING)
{
char *str = (char *)(void *)integerReturnValue;
ficlStackPushInteger(vm->dataStack, integerReturnValue);
ficlStackPushInteger(vm->dataStack, strlen(str));
}
#if FICL_WANT_FLOAT
else if (FICL_MULTICALL_GET_RETURNTYPE(flags) == FICL_MULTICALL_RETURNTYPE_FLOAT)
ficlStackPushFloat(vm->floatStack, floatReturnValue);
#endif /* FICL_WANT_FLOAT */
if (fixups != NULL)
{
for (i = 0; i < fixupCount; i++)
if (fixups[i] != NULL)
free(fixups[i]);
free(fixups);
}
return;
}
/**************************************************************************
f i c l C o m p i l e P l a t f o r m
** Build Win32 platform extensions into the system dictionary
**************************************************************************/
void ficlSystemCompilePlatform(ficlSystem *system)
{
HMODULE hModule;
ficlDictionary *dictionary = system->dictionary;
FICL_SYSTEM_ASSERT(system, dictionary);
/*
** one native function call to rule them all, one native function call to find them,
** one native function call to bring them all and in the darkness bind them.
** --lch (with apologies to j.r.r.t.)
*/
ficlDictionarySetPrimitive(dictionary, "multicall", ficlPrimitiveMulticall, FICL_WORD_DEFAULT);
ficlDictionarySetConstant(dictionary, "multicall-calltype-function", FICL_MULTICALL_CALLTYPE_FUNCTION);
ficlDictionarySetConstant(dictionary, "multicall-calltype-method", FICL_MULTICALL_CALLTYPE_METHOD);
ficlDictionarySetConstant(dictionary, "multicall-calltype-virtual-method", FICL_MULTICALL_CALLTYPE_VIRTUAL_METHOD);
ficlDictionarySetConstant(dictionary, "multicall-returntype-void", FICL_MULTICALL_RETURNTYPE_VOID);
ficlDictionarySetConstant(dictionary, "multicall-returntype-integer", FICL_MULTICALL_RETURNTYPE_INTEGER);
ficlDictionarySetConstant(dictionary, "multicall-returntype-cstring", FICL_MULTICALL_RETURNTYPE_CSTRING);
ficlDictionarySetConstant(dictionary, "multicall-returntype-float", FICL_MULTICALL_RETURNTYPE_FLOAT);
ficlDictionarySetConstant(dictionary, "multicall-reverse-arguments", FICL_MULTICALL_REVERSE_ARGUMENTS);
ficlDictionarySetConstant(dictionary, "multicall-explit-vtable", FICL_MULTICALL_EXPLICIT_VTABLE);
/*
** Every other Win32-specific word is implemented in Ficl, with multicall or whatnot.
** (Give me a lever, and a place to stand, and I will move the Earth.)
** See softcore/win32.fr for details. --lch
*/
hModule = LoadLibrary("kernel32.dll");
ficlDictionarySetConstantPointer(dictionary, "kernel32.dll", hModule);
ficlDictionarySetConstantPointer(dictionary, "(get-proc-address)", GetProcAddress(hModule, "GetProcAddress"));
FreeLibrary(hModule);
return;
}