1. Don Williamson
  2. clReflect

Commits

Don Williamson  committed c1a38c7

Added missing release files.

  • Participants
  • Parent commits 493b7ff
  • Branches default

Comments (0)

Files changed (7)

File LICENSE

View file
-Copyright (c) 2011 Don Williamson
+Copyright (c) 2011-2012 Don Williamson
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in

File release/inc/clutl/JSONLexer.h

View file
+
+#pragma once
+
+
+#include <clcpp/clcpp.h>
+#include <clutl/Serialise.h>
+
+
+namespace clutl
+{
+	enum JSONTokenType
+	{
+		JSON_TOKEN_NONE,
+
+		// Single character tokens match their character values for simpler switch code
+		JSON_TOKEN_LBRACE = '{',
+		JSON_TOKEN_RBRACE = '}',
+		JSON_TOKEN_COMMA = ',',
+		JSON_TOKEN_COLON = ':',
+		JSON_TOKEN_LBRACKET = '[',
+		JSON_TOKEN_RBRACKET = ']',
+
+		JSON_TOKEN_STRING,
+
+		JSON_TOKEN_TRUE,
+		JSON_TOKEN_FALSE,
+		JSON_TOKEN_NULL,
+
+		JSON_TOKEN_INTEGER,
+		JSON_TOKEN_DECIMAL,
+	};
+
+
+	// Partially reflected so that it can be used for reflecting custom serialisation functions
+	clcpp_attr(reflect_part)
+	struct JSONToken
+	{
+		JSONToken()
+			: type(JSON_TOKEN_NONE)
+			, length(0)
+		{
+		}
+
+		explicit JSONToken(JSONTokenType type, int length)
+			: type(type)
+			, length(length)
+		{
+		}
+
+		bool IsValid() const
+		{
+			return type != JSON_TOKEN_NONE;
+		}
+
+		JSONTokenType type;
+		int length;
+
+		// All possible token value representations
+		struct
+		{
+			union
+			{
+				const char* string;
+				__int64 integer;
+				double decimal;
+			};
+		} val;
+	};
+
+
+
+	//
+	// The main lexer/parser context, for keeping tracking of errors and providing a level of
+	// text parsing abstraction above the data buffer.
+	//
+	class JSONContext
+	{
+	public:
+		JSONContext(clutl::ReadBuffer& read_buffer);
+
+		// Consume the given amount of characters in the data buffer, assuming
+		// they have been parsed correctly. The original position before the
+		// consume operation is returned.
+		unsigned int ConsumeChars(int size);
+		unsigned int ConsumeChar();
+
+		// Take a peek at the next N characters in the data buffer
+		const char* PeekChars();
+		char PeekChar();
+
+		// Test to see if reading a specific count of characters would overflow the input
+		// data buffer. Automatically sets the error code as a result.
+		bool ReadOverflows(int size, clutl::JSONError::Code code = clutl::JSONError::UNEXPECTED_END_OF_DATA);
+
+		// How many bytes are left to parse?
+		unsigned int Remaining() const;
+
+		// Record the first error only, along with its position
+		void SetError(clutl::JSONError::Code code);
+
+		// Increment the current line for error reporting
+		void IncLine();
+
+		void PushState(const clutl::JSONToken& token);
+		void PopState(clutl::JSONToken& token);
+
+		clutl::JSONError GetError() const { return m_Error; }
+
+
+	private:
+		// Parsing state
+		clutl::ReadBuffer& m_ReadBuffer;
+		clutl::JSONError m_Error;
+		unsigned int m_Line;
+		unsigned int m_LinePosition;
+
+		// One-level deep parsing state stack
+		unsigned int m_StackPosition;
+		clutl::JSONToken m_StackToken;
+	};
+
+
+	clutl::JSONToken LexerNextToken(clutl::JSONContext& ctx);
+}

File release/inc/clutl/SerialiseFunction.h

View file
+
+//
+// ===============================================================================
+// clReflect, SerialiseFunction.h - Serialisation of function call parameters and
+// ABI-specific call functions for binding to other languages and RPC.
+// -------------------------------------------------------------------------------
+// Copyright (c) 2011 Don Williamson
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+// ===============================================================================
+//
+
+#pragma once
+
+
+#include <clcpp/clcpp.h>
+#include <clutl/Serialise.h>
+
+
+//
+// Two use-cases here:
+//
+//    1. JSON parameter description to binary data, followed by function call
+//    2. Parameters as binary data, serialised to JSON
+//
+// With a function pointer, JSON parameters can be deserialised and executed with that function:
+//
+//   const clpp::Function* function = ...;
+//   ReadBuffer json_parameters = ...;
+//
+//   ParameterObjectCache poc;
+//   if (BuildParameterObjectCache_JSON(poc, function, json_parameters))
+//      CallFunction_x86_32_msvc_cdecl(function, poc.GetParameters());
+//
+// TODO: Needs binary serialisation support.
+//
+namespace clutl
+{
+	//
+	// Contains a list of parameters ready to be passed to a function
+	// Each parameter is represented as a type/pointer pair, describing how the parameter is passed and
+	// where the parameter is located in memory.
+	//
+	class ParameterData
+	{
+	public:
+		static const int MAX_NB_FIELDS = 16;
+
+		struct ParamDesc
+		{
+			const clcpp::Type* type;
+			clcpp::Qualifier::Operator op;
+			void* object;
+		};
+
+		ParameterData();
+
+		// Clears all parameter data
+		void Reset();
+
+		// Adds parameters, in left-to-right call order
+		void PushParameter(const clcpp::Type* type, clcpp::Qualifier::Operator op, void* object);
+
+		unsigned int GetNbParameters() const { return m_NbParameters; }
+		ParamDesc& GetParameter(unsigned int index) { return m_Parameters[index]; }
+		const ParamDesc& GetParameter(unsigned int index) const { return m_Parameters[index]; }
+
+	private:
+		// Parameter array allocated locally
+		char m_ParameterData[MAX_NB_FIELDS * sizeof(ParamDesc)];
+		clcpp::CArray<ParamDesc> m_Parameters;
+		unsigned int m_NbParameters;
+	};
+
+
+	//
+	// When deserialising a chunk of data that has to be passed as to a function as parameters,
+	// this serves as the deserialisation source, allocating and constructing the required objects.
+	//
+	class ParameterObjectCache
+	{
+	public:
+		~ParameterObjectCache();
+
+		// Call to initialise the object cache for a specific function
+		// Can safely be called multiple times with different objects
+		void Init(const clcpp::Function* function);
+
+		// Allocates and constructs a region of memory in the cache for objects of the type specified in the field
+		void* AllocParameter(const clcpp::Field* field);
+
+		ParameterData& GetParameters() { return m_Parameters; }
+		const ParameterData& GetParameters() const { return m_Parameters; }
+
+	private:
+		void DeleteObjects();
+
+		WriteBuffer m_Data;
+		ParameterData m_Parameters;
+	};
+
+
+
+	bool BuildParameterObjectCache_JSON(ParameterObjectCache& poc, const clcpp::Function* function, ReadBuffer& parameter_source);
+
+
+	bool CallFunction_x86_32_msvc_cdecl(const clcpp::Function* function, const ParameterData& parameters);
+	bool CallFunction_x86_32_msvc_thiscall(const clcpp::Function* function, const ParameterData& parameters);
+}

File release/src/clReflectUtil/JSONLexer.cpp

View file
+
+#include <clutl/JSONLexer.h>
+#include "Platform.h"
+
+
+namespace
+{
+	bool isdigit(char c)
+	{
+		return c >= '0' && c <= '9';
+	}
+	bool ishexdigit(char c)
+	{
+		return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+	}
+
+
+	int Lexer32bitHexDigits(clutl::JSONContext& ctx)
+	{
+		// Skip the 'u' and check for overflow
+		ctx.ConsumeChar();
+		if (ctx.ReadOverflows(4))
+			return 0;
+
+		// Ensure the next 4 bytes are hex digits
+		// NOTE: \u is not valid - C has the equivalent \xhh and \xhhhh
+		const char* digits = ctx.PeekChars();
+		if (ishexdigit(digits[0]) &&
+			ishexdigit(digits[1]) &&
+			ishexdigit(digits[2]) &&
+			ishexdigit(digits[3]))
+		{
+			ctx.ConsumeChars(4);
+			return 4;
+		}
+
+		ctx.SetError(clutl::JSONError::EXPECTING_HEX_DIGIT);
+
+		return 0;
+	}
+
+
+	int LexerEscapeSequence(clutl::JSONContext& ctx)
+	{
+		ctx.ConsumeChar();
+
+		if (ctx.ReadOverflows(0))
+			return 0;
+		char c = ctx.PeekChar();
+
+		switch (c)
+		{
+		// Pass all single character sequences
+		case '\"':
+		case '\\':
+		case '/':
+		case 'b':
+		case 'n':
+		case 'f':
+		case 'r':
+		case 't':
+			ctx.ConsumeChar();
+			return 1;
+
+		// Parse the unicode hex digits
+		case 'u':
+			return 1 + Lexer32bitHexDigits(ctx);
+
+		default:
+			ctx.SetError(clutl::JSONError::INVALID_ESCAPE_SEQUENCE);
+			return 0;
+		}
+	}
+
+
+	clutl::JSONToken LexerString(clutl::JSONContext& ctx)
+	{
+		// Start off construction of the string beyond the open quote
+		ctx.ConsumeChar();
+		clutl::JSONToken token(clutl::JSON_TOKEN_STRING, 0);
+		token.val.string = ctx.PeekChars();
+
+		// The common case here is another character as opposed to quotes so
+		// keep looping until that happens
+		int len = 0;
+		while (true)
+		{
+			if (ctx.ReadOverflows(0))
+				return clutl::JSONToken();
+			char c = ctx.PeekChar();
+
+			switch (c)
+			{
+			// The string terminates with a quote
+			case '\"':
+				ctx.ConsumeChar();
+				return token;
+
+			// Escape sequence
+			case '\\':
+				len = LexerEscapeSequence(ctx);
+				if (len == 0)
+					return clutl::JSONToken();
+				token.length += 1 + len;
+				break;
+
+			// A typical string character
+			default:
+				ctx.ConsumeChar();
+				token.length++;
+			}
+		}
+
+		return token;
+	}
+
+
+	//
+	// This will return an integer in the range [-9,223,372,036,854,775,808:9,223,372,036,854,775,807]
+	//
+	bool LexerInteger(clutl::JSONContext& ctx, unsigned __int64& uintval)
+	{
+		// Consume the first digit
+		if (ctx.ReadOverflows(0))
+			return false;
+		char c = ctx.PeekChar();
+		if (!isdigit(c))
+		{
+			ctx.SetError(clutl::JSONError::EXPECTING_DIGIT);
+			return false;
+		}
+
+		uintval = 0;
+		do 
+		{
+			// Consume and accumulate the digit
+			ctx.ConsumeChar();
+			uintval = (uintval * 10) + (c - '0');
+
+			// Peek at the next character and leave if its not a digit
+			if (ctx.ReadOverflows(0))
+				return false;
+			c = ctx.PeekChar();
+		} while (isdigit(c));
+
+		return true;
+	}
+
+
+	clutl::JSONToken LexerHexInteger(clutl::JSONContext& ctx, clutl::JSONToken& token)
+	{
+		unsigned __int64& uintval = (unsigned __int64&)token.val.integer;
+
+		// Consume the first digit
+		if (ctx.ReadOverflows(0))
+			return clutl::JSONToken();
+		char c = ctx.PeekChar();
+		if (!ishexdigit(c))
+		{
+			ctx.SetError(clutl::JSONError::EXPECTING_HEX_DIGIT);
+			return clutl::JSONToken();
+		}
+
+		uintval = 0;
+		do
+		{
+			// Consume and accumulate the digit
+			ctx.ConsumeChar();
+			unsigned __int64 digit = 0;
+			if (c < 'A')
+				digit = c - '0';
+			else
+				digit = (c | 0x20) - 'a' + 10;
+			uintval = (uintval * 16) + digit;
+
+			// Peek at the next character and leave if it's not a hex digit
+			if (ctx.ReadOverflows(0))
+				return clutl::JSONToken();
+			c = ctx.PeekChar();
+		} while (ishexdigit(c));
+
+		return token;
+	}
+
+
+	const char* VerifyDigits(clutl::JSONContext& ctx, const char* decimal, const char* end)
+	{
+		while (true)
+		{
+			if (decimal >= end)
+			{
+				ctx.SetError(clutl::JSONError::UNEXPECTED_END_OF_DATA);
+				return 0;
+			}
+
+			if (!isdigit(*decimal))
+				break;
+
+			decimal++;
+		}
+
+		return decimal;
+	}
+
+
+	bool VerifyDecimal(clutl::JSONContext& ctx, const char* decimal, int len)
+	{
+		// Check that there is stuff beyond the .,e,E
+		if (len < 2)
+		{
+			ctx.SetError(clutl::JSONError::UNEXPECTED_END_OF_DATA);
+			return false;
+		}
+
+		const char* end = decimal + len;
+		if (*decimal++ == '.')
+		{
+			// Ensure there are digits trailing the decimal point
+			decimal = VerifyDigits(ctx, decimal, end);
+			if (decimal == 0)
+				return false;
+
+			// Only need to continue if there's an exponent
+			char c = *decimal++;
+			if (c != 'e' && c != 'E')
+				return true;
+		}
+
+		// Skip over any pos/neg qualifiers
+		char c = *decimal;
+		if (c == '-' || c == '+')
+			decimal++;
+
+		// Ensure there are digits trailing the exponent
+		return VerifyDigits(ctx, decimal, end) != 0;
+	}
+
+
+	clutl::JSONToken LexerNumber(clutl::JSONContext& ctx)
+	{
+		// Start off construction of an integer
+		const char* number_start = ctx.PeekChars();
+		clutl::JSONToken token(clutl::JSON_TOKEN_INTEGER, 0);
+
+		// Is this a hex integer?
+		unsigned __int64 uintval;
+		if (ctx.PeekChar() == '0')
+		{
+			if (ctx.ReadOverflows(1))
+				return clutl::JSONToken();
+
+			// Change the token type to decimal if 'd' is present, relying on the value
+			// union to alias between double/int types
+			switch (ctx.PeekChars()[1])
+			{
+			case 'd':
+				token.type = clutl::JSON_TOKEN_DECIMAL;
+			case 'x':
+				ctx.ConsumeChars(2);
+				return LexerHexInteger(ctx, token);
+			}
+		}
+
+		// Consume negative
+		bool is_negative = false;
+		if (ctx.PeekChar() == '-')
+		{
+			is_negative = true;
+			ctx.ConsumeChar();
+		}
+
+		// Parse integer digits
+		if (!LexerInteger(ctx, uintval))
+			return clutl::JSONToken();
+
+		// Convert to signed integer
+		if (is_negative)
+			token.val.integer = 0ULL - uintval;
+		else
+			token.val.integer = uintval;
+
+		// Is this a decimal?
+		const char* decimal_start = ctx.PeekChars();
+		char c = *decimal_start;
+		if (c == '.' || c == 'e' || c == 'E')
+		{
+			if (!VerifyDecimal(ctx, decimal_start, ctx.Remaining()))
+				return clutl::JSONToken();
+
+			// Re-evaluate as a decimal using the more expensive strtod function
+			char* number_end;
+			token.type = clutl::JSON_TOKEN_DECIMAL;
+			token.val.decimal = strtod(number_start, &number_end);
+
+			// Skip over the parsed decimal
+			ctx.ConsumeChars(number_end - decimal_start);
+		}
+
+		return token;
+	}
+
+
+	clutl::JSONToken LexerKeyword(clutl::JSONContext& ctx, clutl::JSONTokenType type, const char* keyword, int len)
+	{
+		// Consume the matched first letter
+		ctx.ConsumeChar();
+
+		// Try to match the remaining letters of the keyword
+		int dlen = len;
+		while (dlen)
+		{
+			if (ctx.ReadOverflows(0))
+				return clutl::JSONToken();
+
+			// Early out when keyword no longer matches
+			if (*keyword++ != ctx.PeekChar())
+				break;
+
+			ctx.ConsumeChar();
+			dlen--;
+		}
+
+		if (dlen)
+		{
+			ctx.SetError(clutl::JSONError::INVALID_KEYWORD);
+			return clutl::JSONToken();
+		}
+
+		return clutl::JSONToken(type, len + 1);
+	}
+}
+
+
+clutl::JSONContext::JSONContext(clutl::ReadBuffer& read_buffer)
+	: m_ReadBuffer(read_buffer)
+	, m_Line(1)
+	, m_LinePosition(0)
+	, m_StackPosition(0xFFFFFFFF)
+{
+}
+
+unsigned int clutl::JSONContext::ConsumeChars(int size)
+{
+	unsigned int pos = m_ReadBuffer.GetBytesRead();
+	m_ReadBuffer.SeekRel(size);
+	return pos;
+}
+
+
+unsigned int clutl::JSONContext::ConsumeChar()
+{
+	return ConsumeChars(1);
+}
+
+// Take a peek at the next N characters in the data buffer
+const char* clutl::JSONContext::PeekChars()
+{
+	return m_ReadBuffer.ReadAt(m_ReadBuffer.GetBytesRead());
+}
+
+
+char clutl::JSONContext::PeekChar()
+{
+	return *PeekChars();
+}
+
+
+bool clutl::JSONContext::ReadOverflows(int size, clutl::JSONError::Code code)
+{
+	if (m_ReadBuffer.GetBytesRead() + size >= m_ReadBuffer.GetTotalBytes())
+	{
+		SetError(code);
+		return true;
+	}
+	return false;
+}
+
+
+unsigned int clutl::JSONContext::Remaining() const
+{
+	return m_ReadBuffer.GetBytesRemaining();
+}
+
+
+void clutl::JSONContext::SetError(clutl::JSONError::Code code)
+{
+	if (m_Error.code == clutl::JSONError::NONE)
+	{
+		m_Error.code = code;
+		m_Error.position = m_ReadBuffer.GetBytesRead();
+		m_Error.line = m_Line;
+		m_Error.column = m_Error.position - m_LinePosition;
+	}
+}
+
+
+void clutl::JSONContext::IncLine()
+{
+	m_Line++;
+	m_LinePosition = m_ReadBuffer.GetBytesRead();
+}
+
+
+void clutl::JSONContext::PushState(const clutl::JSONToken& token)
+{
+	clcpp::internal::Assert(m_StackPosition == 0xFFFFFFFF);
+
+	// Push
+	m_StackPosition = m_ReadBuffer.GetBytesRead();
+	m_StackToken = token;
+}
+
+
+void clutl::JSONContext::PopState(clutl::JSONToken& token)
+{
+	clcpp::internal::Assert(m_StackPosition != 0xFFFFFFFF);
+
+	// Restore state
+	int offset = m_ReadBuffer.GetBytesRead() - m_StackPosition;
+	m_ReadBuffer.SeekRel(-offset);
+	token = m_StackToken;
+
+	// Pop
+	m_StackPosition = 0xFFFFFFFF;
+	m_StackToken = clutl::JSONToken();
+}
+
+
+clutl::JSONToken clutl::LexerNextToken(clutl::JSONContext& ctx)
+{
+start:
+	// Read the current character and return an empty token at stream end
+	if (ctx.ReadOverflows(0, clutl::JSONError::NONE))
+		return clutl::JSONToken();
+	char c = ctx.PeekChar();
+
+	switch (c)
+	{
+	// Branch to the start only if it's a whitespace (the least-common case)
+	case '\n':
+		ctx.IncLine();
+	case ' ':
+	case '\t':
+	case '\v':
+	case '\f':
+	case '\r':
+		ctx.ConsumeChar();
+		goto start;
+
+	// Structural single character tokens
+	case '{':
+	case '}':
+	case ',':
+	case '[':
+	case ']':
+	case ':':
+		ctx.ConsumeChar();
+		return clutl::JSONToken((clutl::JSONTokenType)c, 1);
+
+	// Strings
+	case '\"':
+		return LexerString(ctx);
+
+	// Integer or floating point numbers
+	case '-':
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		return LexerNumber(ctx);
+
+	// Keywords
+	case 't': return LexerKeyword(ctx, clutl::JSON_TOKEN_TRUE, "rue", 3);
+	case 'f': return LexerKeyword(ctx, clutl::JSON_TOKEN_FALSE, "alse", 4);
+	case 'n': return LexerKeyword(ctx, clutl::JSON_TOKEN_NULL, "ull", 3);
+
+	default:
+		ctx.SetError(clutl::JSONError::UNEXPECTED_CHARACTER);
+		return clutl::JSONToken();
+	}
+}

File release/src/clReflectUtil/Platform.cpp

View file
+
+#include "Platform.h"
+
+
+#if defined(_WINDOWS) || defined(_WIN32)
+	// Windows-specific module loading and inspection functions
+	typedef int (__stdcall *FunctionPtr)();
+	extern "C" __declspec(dllimport) void* __stdcall LoadLibraryA(const char* lpLibFileName);
+	extern "C" __declspec(dllimport) FunctionPtr __stdcall GetProcAddress(void* module, const char* lpProcName);
+	extern "C" __declspec(dllimport) int __stdcall FreeLibrary(void* hLibModule);
+#endif
+
+
+void* LoadSharedLibrary(const char* filename)
+{
+	void* handle = 0;
+#if defined(_WINDOWS) || defined(_WIN32)
+	handle = LoadLibraryA(filename);
+#endif
+	return handle;
+}
+
+
+void* GetSharedLibraryFunction(void* handle, const char* function_name)
+{
+	void* function = 0;
+#if defined(_WINDOWS) || defined(_WIN32)
+	function = GetProcAddress(handle, function_name);
+#endif
+	return function;
+}
+
+
+void FreeSharedLibrary(void* handle)
+{
+#if defined(_WINDOWS) || defined(_WIN32)
+	FreeLibrary(handle);
+#endif
+}

File release/src/clReflectUtil/Platform.h

View file
+
+
+// Private, external platform dependencies for clReflectUtil
+// All dependencies must come through here without this header file invoking platform-specific header files.
+
+
+// Standard C library function, convert string to double-precision number
+// http://pubs.opengroup.org/onlinepubs/007904975/functions/strtod.html
+extern "C" double strtod(const char* s00, char** se);
+
+
+// Standard C library function, copy bytes
+// http://pubs.opengroup.org/onlinepubs/009695399/functions/memcpy.html
+extern "C" void* memcpy(void* dst, const void* src, unsigned int size);
+
+
+// Non-standard, writes at most n bytes to dest with printf formatting
+// On Win32/MSVC, this usually maps to _snprintf_s
+extern "C" int snprintf(char* dest, unsigned int n, const char* fmt, ...);
+
+
+// Share library (DLL/SO) implementations
+void* LoadSharedLibrary(const char* filename);
+void* GetSharedLibraryFunction(void* handle, const char* function_name);
+void FreeSharedLibrary(void* handle);

File release/src/clReflectUtil/SerialiseFunction.cpp

View file
+
+#include <clutl/SerialiseFunction.h>
+#include <clutl/JSONLexer.h>
+#include <clcpp/FunctionCall.h>
+#include "Platform.h"
+
+
+namespace
+{
+	unsigned int ParamAllocSize(const clcpp::Field* field)
+	{
+		unsigned int param_size = field->type->size;
+		if (field->qualifier.op == clcpp::Qualifier::POINTER)
+			param_size = sizeof(void*);
+		return param_size;
+	}
+}
+
+
+clutl::ParameterData::ParameterData()
+	: m_Parameters(m_ParameterData, MAX_NB_FIELDS)
+	, m_NbParameters(0)
+{
+}
+
+
+void clutl::ParameterData::Reset()
+{
+	m_NbParameters = 0;
+}
+
+
+void clutl::ParameterData::PushParameter(const clcpp::Type* type, clcpp::Qualifier::Operator op, void* object)
+{
+	ParamDesc& param = m_Parameters[m_NbParameters++];
+	param.type = type;
+	param.op = op;
+	param.object = object;
+}
+
+
+clutl::ParameterObjectCache::~ParameterObjectCache()
+{
+	DeleteObjects();
+}
+
+
+void clutl::ParameterObjectCache::Init(const clcpp::Function* function)
+{
+	DeleteObjects();
+
+	// Calculate the total space occupied by parameters
+	unsigned int total_param_size = 0;
+	for (int i = 0; i < function->parameters.size(); i++)
+		total_param_size += ParamAllocSize(function->parameters[i]);
+
+	// Pre-allocate the data for the parameters
+	m_Data.Reset();
+	m_Data.Alloc(total_param_size);
+
+	// And put all write pointers back at the beginning
+	m_Data.Reset();
+	m_Parameters.Reset();
+}
+
+
+void* clutl::ParameterObjectCache::AllocParameter(const clcpp::Field* field)
+{
+	// Allocate the space for the parameter
+	unsigned int param_size = ParamAllocSize(field);
+	void* param_object = m_Data.Alloc(param_size);
+
+	// Call any class constructors
+	if (field->type->kind == clcpp::Primitive::KIND_CLASS && field->qualifier.op != clcpp::Qualifier::POINTER)
+	{
+		const clcpp::Class* class_type = field->type->AsClass();
+		if (class_type->constructor)
+			CallFunction(class_type->constructor, param_object);
+	}
+
+	// Keep track of the parameter before its written to
+	m_Parameters.PushParameter(field->type, field->qualifier.op, param_object);
+
+	return param_object;
+}
+
+
+void clutl::ParameterObjectCache::DeleteObjects()
+{
+	// Call destructor on every known parameter
+	unsigned int nb_params = m_Parameters.GetNbParameters();
+	for (unsigned int i = 0; i < nb_params; i++)
+	{
+		const ParameterData::ParamDesc& param = m_Parameters.GetParameter(i);
+		if (param.type->kind == clcpp::Primitive::KIND_CLASS && param.op != clcpp::Qualifier::POINTER)
+		{
+			const clcpp::Class* class_type = param.type->AsClass();
+			if (class_type->destructor)
+				CallFunction(class_type->destructor, param.object);
+		}
+	}
+}
+
+
+bool clutl::BuildParameterObjectCache_JSON(clutl::ParameterObjectCache& poc, const clcpp::Function* function, clutl::ReadBuffer& parameter_source)
+{
+	// Reuse the incoming cache
+	poc.Init(function);
+
+	// A local cache of all function parameters in their sorted order
+	char sorted_field_data[ParameterData::MAX_NB_FIELDS * sizeof(const clcpp::Field*)];
+	clcpp::CArray<const clcpp::Field*> sorted_fields(sorted_field_data, ParameterData::MAX_NB_FIELDS);
+
+	// Sort each parameter into its call order
+	unsigned int nb_fields = function->parameters.size();
+	for (unsigned int i = 0; i < nb_fields; i++)
+	{
+		const clcpp::Field* field = function->parameters[i];
+		sorted_fields[field->offset] = field;
+	}
+
+	// Check for parameter opening list
+	JSONContext ctx(parameter_source);
+	JSONToken t = LexerNextToken(ctx);
+	if (t.type != JSON_TOKEN_LBRACKET)
+		return false;
+
+	// Allocate parameters for each field
+	for (unsigned i = 0; i < nb_fields; i++)
+	{
+		const clcpp::Field* field = sorted_fields[i];
+		void* param_object = poc.AllocParameter(field);
+
+		// Parse the JSON parameter and move onto the next one
+		JSONError error = LoadJSON(ctx, param_object, field);
+		if (error.code != clutl::JSONError::NONE)
+			return false;
+	}
+
+	// TODO: What about patching up pointers?
+
+	return true;
+}
+
+
+#ifdef _MSC_VER
+
+
+static bool CallFunction_x86_32_msvc(const clcpp::Function* function, const clutl::ParameterData& parameters, bool thiscall)
+{
+	// Ensure parameter count matches
+	unsigned int nb_params = function->parameters.size();
+	if (nb_params != parameters.GetNbParameters())
+		return false;
+
+	int last_param = 0;
+	if (thiscall)
+		last_param = 1;
+
+	// Walk over the parameters, pushing them on the native stack in right-to-left order
+	unsigned int stack_size = 0;
+	for (int i = nb_params - 1; i >= last_param; i--)
+	{
+		const clutl::ParameterData::ParamDesc& param = parameters.GetParameter(i);
+
+		if (param.op == clcpp::Qualifier::POINTER)
+		{
+			// Directly push the pointer value
+			void* param_object = *(void**)param.object;
+			__asm push param_object
+			stack_size += sizeof(void*);
+		}
+
+		else if (param.op == clcpp::Qualifier::REFERENCE)
+		{
+			void* param_object = param.object;
+			__asm push param_object
+			stack_size += sizeof(void*);
+		}
+
+		else
+		{
+			unsigned int param_size = param.type->size;
+			void* param_object = param.object;
+
+			__asm
+			{
+				// Get the parameter size, round up to 32-bit alignment and allocate some stack space
+				mov ecx, param_size
+				add ecx, 3
+				and ecx, 0FFFFFFFCh
+				sub esp, ecx
+				add stack_size, ecx
+
+				// Copy the object
+				shr ecx, 2
+				mov edi, esp
+				mov esi, param_object
+				rep movsd
+			}
+		}
+	}
+
+	// Call the function
+	unsigned int function_address = function->address;
+	if (thiscall)
+	{
+		const clutl::ParameterData::ParamDesc& param = parameters.GetParameter(0);
+		void* param_object = *(void**)param.object;
+
+		// Only call if this pointer is valid
+		if (param_object)
+		{
+			__asm
+			{
+				mov ecx, param_object
+				mov ebx, function_address
+				call ebx
+			}
+		}
+	}
+	else
+	{
+		__asm
+		{
+			call function_address
+			add esp, stack_size
+		}
+	}
+
+	// TODO: Need to do bounds checking here?
+
+	return true;
+}
+
+
+bool clutl::CallFunction_x86_32_msvc_cdecl(const clcpp::Function* function, const ParameterData& parameters)
+{
+	unsigned int nb_params = function->parameters.size();
+	if (nb_params != parameters.GetNbParameters())
+		return false;
+
+	return CallFunction_x86_32_msvc(function, parameters, false);
+}
+
+
+bool clutl::CallFunction_x86_32_msvc_thiscall(const clcpp::Function* function, const clutl::ParameterData& parameters)
+{
+	unsigned int nb_params = function->parameters.size();
+	if (nb_params != parameters.GetNbParameters())
+		return false;
+
+	return CallFunction_x86_32_msvc(function, parameters, true);
+}
+
+
+#endif