diff --git a/core/logic/AMBuilder b/core/logic/AMBuilder
index 08d086b3..3e90dc55 100644
--- a/core/logic/AMBuilder
+++ b/core/logic/AMBuilder
@@ -82,6 +82,7 @@ binary.sources += [
'CDataPack.cpp',
'frame_tasks.cpp',
'smn_halflife.cpp',
+ 'FrameIterator.cpp',
]
if builder.target.platform == 'windows':
binary.sources += ['thread/WinThreads.cpp']
diff --git a/core/logic/FrameIterator.cpp b/core/logic/FrameIterator.cpp
new file mode 100644
index 00000000..f2a939c0
--- /dev/null
+++ b/core/logic/FrameIterator.cpp
@@ -0,0 +1,96 @@
+/**
+ * vim: set ts=4 sw=4 tw=99 noet :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2017 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include "FrameIterator.h"
+
+SafeFrameIterator::SafeFrameIterator(IFrameIterator *it)
+{
+ while (!it->Done())
+ {
+ FrameInfo info = FrameInfo(it);
+ frames.append(info);
+ it->Next();
+ }
+
+ it->Reset();
+ current = 0;
+}
+
+bool SafeFrameIterator::Done() const
+{
+ return current == frames.length();
+}
+
+bool SafeFrameIterator::Next()
+{
+ if (!this->Done())
+ {
+ current++;
+ return true;
+ }
+
+ return false;
+}
+
+void SafeFrameIterator::Reset()
+{
+ current = 0;
+}
+
+int SafeFrameIterator::LineNumber() const
+{
+ if (this->Done())
+ {
+ return -1;
+ }
+
+ return (int)frames[current].LineNumber;
+}
+
+const char *SafeFrameIterator::FunctionName() const
+{
+ if (this->Done())
+ {
+ return NULL;
+ }
+
+ return frames[current].FunctionName.chars();
+}
+
+const char *SafeFrameIterator::FilePath() const
+{
+ if (this->Done())
+ {
+ return NULL;
+ }
+
+ return frames[current].FilePath.chars();
+}
diff --git a/core/logic/FrameIterator.h b/core/logic/FrameIterator.h
new file mode 100644
index 00000000..b0c2ce17
--- /dev/null
+++ b/core/logic/FrameIterator.h
@@ -0,0 +1,77 @@
+/**
+ * vim: set ts=4 sw=4 tw=99 noet :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2017 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include "sp_vm_api.h"
+#include
+#include
+
+using namespace SourcePawn;
+
+/**
+ * Frame iterator cache which is safe for plugins to hold handles to,
+ * unlike what you'd recieve from IPluginContext::CreateFrameIterator.
+ */
+class SafeFrameIterator
+{
+public:
+
+ /**
+ * Struct which holds all of the cached values for each individual frame.
+ */
+ struct FrameInfo
+ {
+ ke::AString FunctionName;
+ ke::AString FilePath;
+ unsigned LineNumber;
+
+ FrameInfo(IFrameIterator *it)
+ {
+ LineNumber = it->LineNumber();
+
+ FunctionName = it->FunctionName();
+ FilePath = it->FilePath();
+ }
+ };
+
+ SafeFrameIterator(IFrameIterator *);
+
+ bool Done() const;
+ bool Next();
+ void Reset();
+
+ int LineNumber() const;
+ const char *FunctionName() const;
+ const char *FilePath() const;
+
+private:
+ size_t current;
+ ke::Vector frames;
+};
\ No newline at end of file
diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp
index d12e0fca..06527d7e 100644
--- a/core/logic/smn_core.cpp
+++ b/core/logic/smn_core.cpp
@@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
- * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * Copyright (C) 2004-2017 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
@@ -37,6 +37,8 @@
#include
#include
+#include
+#include
#include
#include
@@ -59,6 +61,7 @@ using namespace SourcePawn;
HandleType_t g_PlIter;
+HandleType_t g_FrameIter;
IForward *g_OnLogAction = NULL;
@@ -76,6 +79,7 @@ public:
hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
g_PlIter = handlesys->CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
+ g_FrameIter = handlesys->CreateType("FrameIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_OnLogAction = forwardsys->CreateForward("OnLogAction",
ET_Hook,
@@ -91,13 +95,21 @@ public:
}
void OnHandleDestroy(HandleType_t type, void *object)
{
- IPluginIterator *iter = (IPluginIterator *)object;
- iter->Release();
+ if (type == g_FrameIter)
+ {
+ delete (SafeFrameIterator *) object;
+ }
+ else if (type == g_PlIter)
+ {
+ IPluginIterator *iter = (IPluginIterator *)object;
+ iter->Release();
+ }
}
void OnSourceModShutdown()
{
forwardsys->ReleaseForward(g_OnLogAction);
handlesys->RemoveType(g_PlIter, g_pCoreIdent);
+ handlesys->RemoveType(g_FrameIter, g_pCoreIdent);
}
} g_CoreNativeHelpers;
@@ -780,6 +792,143 @@ static cell_t IsNullString(IPluginContext *pContext, const cell_t *params)
return str == nullptr;
}
+static cell_t FrameIterator_Create(IPluginContext *pContext, const cell_t *params)
+{
+ IFrameIterator *it = pContext->CreateFrameIterator();
+
+ SafeFrameIterator *iterator = new SafeFrameIterator(it);
+
+ pContext->DestroyFrameIterator(it);
+
+ Handle_t handle = handlesys->CreateHandle(g_FrameIter, iterator, pContext->GetIdentity(), g_pCoreIdent, NULL);
+ if (handle == BAD_HANDLE)
+ {
+ delete iterator;
+ return BAD_HANDLE;
+ }
+
+ return handle;
+}
+
+static cell_t FrameIterator_Next(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ SafeFrameIterator *iterator;
+
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = pContext->GetIdentity();
+
+ if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
+ }
+
+ return iterator->Next();
+}
+
+static cell_t FrameIterator_Reset(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ SafeFrameIterator *iterator;
+
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = pContext->GetIdentity();
+
+ if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
+ }
+
+ iterator->Reset();
+ return 0;
+}
+
+static cell_t FrameIterator_LineNumber(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ SafeFrameIterator *iterator;
+
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = pContext->GetIdentity();
+
+ if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
+ }
+
+ int lineNum = iterator->LineNumber();
+ if (lineNum < 0)
+ {
+ return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
+ }
+
+ return lineNum;
+}
+
+static cell_t FrameIterator_GetFunctionName(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ SafeFrameIterator *iterator;
+
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = pContext->GetIdentity();
+
+ if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
+ }
+
+ const char* functionName = iterator->FunctionName();
+ if (!functionName)
+ {
+ return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
+ }
+
+ char* buffer;
+ pContext->LocalToString(params[2], &buffer);
+ size_t size = static_cast(params[3]);
+
+ ke::SafeStrcpy(buffer, size, functionName);
+ return 0;
+}
+
+static cell_t FrameIterator_GetFilePath(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ SafeFrameIterator *iterator;
+
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = pContext->GetIdentity();
+
+ if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
+ }
+
+ const char* filePath = iterator->FilePath();
+ if (!filePath)
+ {
+ return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
+ }
+
+ char* buffer;
+ pContext->LocalToString(params[2], &buffer);
+ size_t size = static_cast(params[3]);
+
+ ke::SafeStrcpy(buffer, size, filePath);
+ return 0;
+}
+
REGISTER_NATIVES(coreNatives)
{
{"ThrowError", ThrowError},
@@ -810,5 +959,12 @@ REGISTER_NATIVES(coreNatives)
{"StoreToAddress", StoreToAddress},
{"IsNullVector", IsNullVector},
{"IsNullString", IsNullString},
+
+ {"FrameIterator.FrameIterator", FrameIterator_Create},
+ {"FrameIterator.Next", FrameIterator_Next},
+ {"FrameIterator.Reset", FrameIterator_Reset},
+ {"FrameIterator.LineNumber.get", FrameIterator_LineNumber},
+ {"FrameIterator.GetFunctionName", FrameIterator_GetFunctionName},
+ {"FrameIterator.GetFilePath", FrameIterator_GetFilePath},
{NULL, NULL},
};
diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc
index 023e3af5..3b188630 100644
--- a/plugins/include/sourcemod.inc
+++ b/plugins/include/sourcemod.inc
@@ -645,6 +645,37 @@ native int LoadFromAddress(Address addr, NumberType size);
*/
native void StoreToAddress(Address addr, int data, NumberType size);
+methodmap FrameIterator < Handle {
+ // Creates a stack frame iterator to build your own stack traces.
+ // @return New handle to a FrameIterator.
+ public native FrameIterator();
+
+ // Advances the iterator to the next stack frame.
+ // @return True if another frame was fetched and data can be successfully read.
+ // @error No next element exception.
+ public native bool Next();
+
+ // Resets the iterator back to it's starting position.
+ public native void Reset();
+
+ // Returns the line number of the current function call.
+ property bool LineNumber {
+ public native get();
+ }
+
+ // Gets the name of the current function in the call stack.
+ //
+ // @param buffer Buffer to copy to.
+ // @param maxlen Max size of the buffer.
+ public native void GetFunctionName(char[] buffer, int maxlen);
+
+ // Gets the file path to the current call in the call stack.
+ //
+ // @param buffer Buffer to copy to.
+ // @param maxlen Max size of the buffer.
+ public native void GetFilePath(char[] buffer, int maxlen);
+}
+
#include
#include
#include