// vim: set sts=2 ts=8 sw=2 tw=99 et: // // Copyright (C) 2006-2015 AlliedModders LLC // // This file is part of SourcePawn. SourcePawn is free software: you can // redistribute it and/or modify it under the terms of the GNU General Public // License as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // You should have received a copy of the GNU General Public License along with // SourcePawn. If not, see http://www.gnu.org/licenses/. // #include #include #include "scripted-invoker.h" #include "plugin-runtime.h" /******************** * FUNCTION CALLING * ********************/ ScriptedInvoker::~ScriptedInvoker() { delete [] full_name_; } bool ScriptedInvoker::IsRunnable() { return !m_pRuntime->IsPaused(); } int ScriptedInvoker::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result) { return CallFunction2(m_pRuntime->GetDefaultContext(), params, num_params, result); } int ScriptedInvoker::CallFunction2(IPluginContext *pContext, const cell_t *params, unsigned int num_params, cell_t *result) { return pContext->Execute2(this, params, num_params, result); } IPluginContext * ScriptedInvoker::GetParentContext() { return m_pRuntime->GetDefaultContext(); } ScriptedInvoker::ScriptedInvoker(PluginRuntime *runtime, funcid_t id, uint32_t pub_id) : m_curparam(0), m_errorstate(SP_ERROR_NONE), m_FnId(id) { m_pRuntime = runtime; runtime->GetPublicByIndex(pub_id, &public_); size_t rt_len = strlen(runtime->plugin()->name); size_t len = rt_len + strlen("::") + strlen(public_->name); full_name_ = new char[len + 1]; strcpy(full_name_, runtime->plugin()->name); strcpy(&full_name_[rt_len], "::"); strcpy(&full_name_[rt_len + 2], public_->name); } int ScriptedInvoker::PushCell(cell_t cell) { if (m_curparam >= SP_MAX_EXEC_PARAMS) return SetError(SP_ERROR_PARAMS_MAX); m_info[m_curparam].marked = false; m_params[m_curparam] = cell; m_curparam++; return SP_ERROR_NONE; } int ScriptedInvoker::PushCellByRef(cell_t *cell, int flags) { return PushArray(cell, 1, flags); } int ScriptedInvoker::PushFloat(float number) { cell_t val = *(cell_t *)&number; return PushCell(val); } int ScriptedInvoker::PushFloatByRef(float *number, int flags) { return PushCellByRef((cell_t *)number, flags); } int ScriptedInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback) { if (m_curparam >= SP_MAX_EXEC_PARAMS) { return SetError(SP_ERROR_PARAMS_MAX); } ParamInfo *info = &m_info[m_curparam]; info->flags = inarray ? copyback : 0; info->marked = true; info->size = cells; info->str.is_sz = false; info->orig_addr = inarray; m_curparam++; return SP_ERROR_NONE; } int ScriptedInvoker::PushString(const char *string) { return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1); } int ScriptedInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags) { return _PushString(buffer, sz_flags, cp_flags, length); } int ScriptedInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len) { if (m_curparam >= SP_MAX_EXEC_PARAMS) return SetError(SP_ERROR_PARAMS_MAX); ParamInfo *info = &m_info[m_curparam]; info->marked = true; info->orig_addr = (cell_t *)string; info->flags = cp_flags; info->size = len; info->str.sz_flags = sz_flags; info->str.is_sz = true; m_curparam++; return SP_ERROR_NONE; } void ScriptedInvoker::Cancel() { if (!m_curparam) return; m_errorstate = SP_ERROR_NONE; m_curparam = 0; } int ScriptedInvoker::Execute(cell_t *result) { return Execute2(m_pRuntime->GetDefaultContext(), result); } int ScriptedInvoker::Execute2(IPluginContext *ctx, cell_t *result) { int err = SP_ERROR_NONE; if (!IsRunnable()) m_errorstate = SP_ERROR_NOT_RUNNABLE; if (m_errorstate != SP_ERROR_NONE) { err = m_errorstate; Cancel(); return err; } //This is for re-entrancy! cell_t temp_params[SP_MAX_EXEC_PARAMS]; ParamInfo temp_info[SP_MAX_EXEC_PARAMS]; unsigned int numparams = m_curparam; unsigned int i; bool docopies = true; if (numparams) { //Save the info locally, then reset it for re-entrant calls. memcpy(temp_info, m_info, numparams * sizeof(ParamInfo)); } m_curparam = 0; /* Browse the parameters and build arrays */ for (i=0; iHeapAlloc(temp_info[i].size, &(temp_info[i].local_addr), &(temp_info[i].phys_addr))) != SP_ERROR_NONE) { break; } if (temp_info[i].orig_addr) { memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size); } } else { /* Calculate cells required for the string */ size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t); /* Allocate the buffer */ if ((err=ctx->HeapAlloc(cells, &(temp_info[i].local_addr), &(temp_info[i].phys_addr))) != SP_ERROR_NONE) { break; } /* Copy original string if necessary */ if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL)) { /* Cut off UTF-8 properly */ if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8) { if ((err=ctx->StringToLocalUTF8(temp_info[i].local_addr, temp_info[i].size, (const char *)temp_info[i].orig_addr, NULL)) != SP_ERROR_NONE) { break; } } /* Copy a binary blob */ else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY) { memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size); } /* Copy ASCII characters */ else { if ((err=ctx->StringToLocal(temp_info[i].local_addr, temp_info[i].size, (const char *)temp_info[i].orig_addr)) != SP_ERROR_NONE) { break; } } } } /* End array/string calculation */ /* Update the pushed parameter with the byref local address */ temp_params[i] = temp_info[i].local_addr; } else { /* Just copy the value normally */ temp_params[i] = m_params[i]; } } /* Make the call if we can */ if (err == SP_ERROR_NONE) { if ((err = CallFunction2(ctx, temp_params, numparams, result)) != SP_ERROR_NONE) docopies = false; } else { docopies = false; } /* i should be equal to the last valid parameter + 1 */ while (i--) { if (!temp_info[i].marked) continue; if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK)) { if (temp_info[i].orig_addr) { if (temp_info[i].str.is_sz) { memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size); } else { if (temp_info[i].size == 1) { *temp_info[i].orig_addr = *(temp_info[i].phys_addr); } else { memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size * sizeof(cell_t)); } } } } if ((err=ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE) return err; } return err; } IPluginRuntime * ScriptedInvoker::GetParentRuntime() { return m_pRuntime; } funcid_t ScriptedInvoker::GetFunctionID() { return m_FnId; } int ScriptedInvoker::SetError(int err) { m_errorstate = err; return err; }