AGS: cc_error returns stack from all script threads

From upstream 9e76800d668d8d0af7d47d815e6d1b781c7601f6
This commit is contained in:
Paul Gilbert 2022-05-04 22:35:51 -07:00
parent 16d0d89787
commit 8e132f7b3f
13 changed files with 114 additions and 109 deletions

View File

@ -1675,7 +1675,7 @@ void prepare_room_sprites() {
if (_G(walkBehindMethod) == DrawAsSeparateSprite) {
for (int wb = 1 /* 0 is "no area" */;
(wb < MAX_WALK_BEHINDS) && (wb < _GP(walkbehindobj).size()); ++wb) {
(wb < MAX_WALK_BEHINDS) && (wb < (int)_GP(walkbehindobj).size()); ++wb) {
const auto &wbobj = _GP(walkbehindobj)[wb];
if (wbobj.Ddb) {
add_to_sprite_list(wbobj.Ddb, wbobj.Pos.X, wbobj.Pos.Y,

View File

@ -315,24 +315,13 @@ void debug_script_log(const char *msg, ...) {
debug_script_print_impl(full_msg, kDbgMsg_Debug);
}
String get_cur_script(int numberOfLinesOfCallStack) {
String callstack;
ccInstance *sci = ccInstance::GetCurrentInstance();
if (sci)
callstack = sci->GetCallStack(numberOfLinesOfCallStack);
if (callstack.IsEmpty())
callstack = cc_get_error().CallStack;
return callstack;
}
struct Breakpoint {
char scriptName[80];
int lineNumber;
};
bool send_message_to_editor(const char *msg, const char *errorMsg) {
String callStack = get_cur_script(25);
String callStack = cc_get_error().CallStack;
if (callStack.IsEmpty())
return false;

View File

@ -32,8 +32,6 @@ struct ScriptPosition;
int check_for_messages_from_editor();
bool send_message_to_editor(const char *msg);
bool send_exception_to_editor(const char *qmsg);
// Returns current script's location and callstack
AGS::Shared::String get_cur_script(int numberOfLinesOfCallStack);
void check_debug_keys();
#define DBG_NOIFACE 1

View File

@ -48,6 +48,7 @@
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/engine/platform/base/sys_main.h"
#include "ags/plugins/plugin_engine.h"
#include "ags/shared/script/cc_common.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/globals.h"
#include "ags/ags.h"
@ -114,7 +115,7 @@ QuitReason quit_check_for_error_state(const char *&qmsg, String &alertis) {
"(ACI version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
}
alertis.Append(get_cur_script(5));
alertis.Append(cc_get_error().CallStack);
if (qreason != kQuit_UserAbort)
alertis.Append("\nError: ");
@ -125,7 +126,7 @@ QuitReason quit_check_for_error_state(const char *&qmsg, String &alertis) {
qmsg++;
alertis.Format("A warning has been generated. This is not normally fatal, but you have selected "
"to treat warnings as errors.\n"
"(ACI version %s)\n\n%s\n", _G(EngineVersion).LongString.GetCStr(), get_cur_script(5).GetCStr());
"(ACI version %s)\n\n%s\n", _G(EngineVersion).LongString.GetCStr(), cc_get_error().CallStack.GetCStr());
return kQuit_GameWarning;
} else {
alertis.Format("An internal error has occurred. Please note down the following information.\n"

View File

@ -157,6 +157,18 @@ const char *regnames[] = { "null", "sp", "mar", "ax", "bx", "cx", "op", "dx" };
const char *fixupnames[] = { "null", "fix_gldata", "fix_func", "fix_string", "fix_import", "fix_datadata", "fix_stack" };
String cc_get_callstack(int max_lines) {
String callstack;
for (auto sci = _GP(InstThreads).crbegin(); sci != _GP(InstThreads).crend(); ++sci) {
if (callstack.IsEmpty())
callstack.Append("in the active script:\n");
else
callstack.Append("in the waiting script:\n");
callstack.Append((*sci)->GetCallStack(max_lines));
}
return callstack;
}
// Function call stack is used to temporarily store
// values before passing them to script function
#define MAX_FUNC_PARAMS 20
@ -181,7 +193,7 @@ struct FunctionCallStack {
ccInstance *ccInstance::GetCurrentInstance() {
return _GP(InstThreads).size() > 0 ? _GP(InstThreads).top() : nullptr;
return _GP(InstThreads).size() > 0 ? _GP(InstThreads).back() : nullptr;
}
ccInstance *ccInstance::CreateFromScript(PScript scri) {
@ -335,14 +347,14 @@ int ccInstance::CallScriptFunction(const char *funcname, int32_t numargs, const
}
PushValueToStack(RuntimeScriptValue().SetInt32(0)); // return address on stack
_GP(InstThreads).push(this); // push instance thread
_GP(InstThreads).push_back(this); // push instance thread
runningInst = this;
int reterr = Run(startat);
// Cleanup before returning, even if error
ASSERT_STACK_SIZE(numargs);
PopValuesFromStack(numargs);
pc = 0;
_GP(InstThreads).pop(); // pop instance thread
_GP(InstThreads).pop_back(); // pop instance thread
if (reterr != 0)
return reterr;
@ -1198,7 +1210,7 @@ int ccInstance::Run(int32_t curpc) {
}
}
String ccInstance::GetCallStack(int maxLines) {
String ccInstance::GetCallStack(int maxLines) const {
String buffer = String::FromFormat("in \"%s\", line %d\n", runningInst->instanceof->GetSectionName(pc), line_number);
int linesDone = 0;
@ -1212,13 +1224,13 @@ String ccInstance::GetCallStack(int maxLines) {
return buffer;
}
void ccInstance::GetScriptPosition(ScriptPosition &script_pos) {
void ccInstance::GetScriptPosition(ScriptPosition &script_pos) const {
script_pos.Section = runningInst->instanceof->GetSectionName(pc);
script_pos.Line = line_number;
}
// get a pointer to a variable or function exported by the script
RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) {
RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) const {
int k;
char altName[200];
snprintf(altName, sizeof(altName), "%s$", symname);
@ -1234,7 +1246,7 @@ RuntimeScriptValue ccInstance::GetSymbolAddress(const char *symname) {
return rval_null;
}
void ccInstance::DumpInstruction(const ScriptOperation &op) {
void ccInstance::DumpInstruction(const ScriptOperation &op) const {
// line_num local var should be shared between all the instances
static int line_num = 0;

View File

@ -159,12 +159,12 @@ public:
int CallScriptFunction(const char *funcname, int32_t num_params, const RuntimeScriptValue *params);
// Get the script's execution position and callstack as human-readable text
Shared::String GetCallStack(int maxLines);
Shared::String GetCallStack(int max_lines = INT_MAX) const;
// Get the script's execution position
void GetScriptPosition(ScriptPosition &script_pos);
void GetScriptPosition(ScriptPosition &script_pos) const;
// Get the address of an exported symbol (function or variable) in the script
RuntimeScriptValue GetSymbolAddress(const char *symname);
void DumpInstruction(const ScriptOperation &op);
RuntimeScriptValue GetSymbolAddress(const char *symname) const;
void DumpInstruction(const ScriptOperation &op) const;
// Tells whether this instance is in the process of executing the byte-code
bool IsBeingRun() const;

View File

@ -564,7 +564,7 @@ void quit_with_script_error(const char *functionName) {
quitprintf("!Error running function '%s':\n%s", functionName, error.ErrorString.GetCStr());
else
quitprintf("Error running function '%s':\n%s\n\n%s", functionName,
error.ErrorString.GetCStr(), get_cur_script(5).GetCStr());
error.ErrorString.GetCStr(), error.CallStack.GetCStr());
}
int get_nivalue(InteractionCommandList *nic, int idx, int parm) {

View File

@ -1,63 +0,0 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
//=============================================================================
//
// Script Editor run-time engine component (c) 1998 Chris Jones
// script chunk format:
// 00h 1 dword version - should be 2
// 04h 1 dword sizeof(scriptblock)
// 08h 1 dword number of ScriptBlocks
// 0Ch n STRUCTs ScriptBlocks
//
//=============================================================================
#include "ags/lib/std/utility.h"
#include "ags/engine/script/cc_instance.h"
#include "ags/shared/script/cc_common.h"
#include "ags/shared/util/file.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class RoomStruct;
} // namespace Shared
} // namespace AGS
using namespace AGS::Shared;
std::pair<String, String> cc_error_at_line(const char *error_msg) {
ccInstance *sci = ccInstance::GetCurrentInstance();
if (!sci) {
return std::make_pair(String::FromFormat("Error (line %d): %s", _G(currentline), error_msg), String());
} else {
return std::make_pair(String::FromFormat("Error: %s\n", error_msg), ccInstance::GetCurrentInstance()->GetCallStack(5));
}
}
String cc_error_without_line(const char *error_msg) {
return String::FromFormat("Runtime error: %s", error_msg);
}
} // namespace AGS3

View File

@ -129,7 +129,7 @@ Globals::Globals() {
_animbuts = new std::vector<AnimatingGUIButton>();
// cc_instance.cpp globals
_InstThreads = new std::stack<ccInstance *>();
_InstThreads = new std::deque<ccInstance *>();
_GlobalReturnValue = new RuntimeScriptValue();
// cc_options.cpp globals

View File

@ -25,7 +25,7 @@
#include "ags/shared/core/platform.h"
#define AGS_PLATFORM_DEFINES_PSP_VARS (AGS_PLATFORM_OS_IOS || AGS_PLATFORM_OS_ANDROID)
#include "ags/lib/std/stack.h"
#include "ags/lib/std/queue.h"
#include "ags/shared/ac/game_version.h"
#include "ags/shared/util/stdio_compat.h"
#include "ags/shared/util/string.h"
@ -369,7 +369,7 @@ public:
// In AGS currently only one thread is running, others are waiting in the queue.
// An example situation is repeatedly_execute_always callback running while
// another instance is waiting at the blocking action or Wait().
std::stack<ccInstance *> *_InstThreads;
std::deque<ccInstance *> *_InstThreads;
// [IKM] 2012-10-21:
// NOTE: This is temporary solution (*sigh*, one of many) which allows certain
// exported functions return value as a RuntimeScriptValue object;

View File

@ -63,6 +63,80 @@ public:
}
};
template<class T>
class deque {
private:
vector<T> _intern;
public:
deque() = default;
typedef typename vector<T>::iterator iterator;
typedef typename const vector<T>::const_iterator const_iterator;
typedef typename vector<T>::reverse_iterator reverse_iterator;
typedef typename const vector<T>::const_reverse_iterator const_reverse_iterator;
void clear() {
_intern.clear();
}
void insert(const T &item) {
_intern.push_back(item);
}
void push_back(const T &item) {
_intern.push_back(item);
}
void push_front(const T &item) {
_intern.push_front(item);
}
void pop_back() {
_intern.pop_back();
}
void pop_front() {
_intern.remove_at(0);
}
const T &front() const {
return _intern.front();
}
const T &back() const {
return _intern.back();
}
void resize(size_t newSize) {
_intern.resize(newSize);
}
size_t size() const {
return _intern.size();
}
T at(size_t idx) {
return _intern[idx];
}
const_iterator cbegin() {
return _intern.cbegin();
}
const_iterator cend() {
return _intern.cend();
}
reverse_iterator rbegin() {
return _intern.rbegin();
}
reverse_iterator rend() {
return _intern.rend();
}
const_reverse_iterator rbegin() const {
return _intern.rbegin();
}
const_reverse_iterator rend() const {
return _intern.rend();
}
const_reverse_iterator crbegin() const {
return _intern.crbegin();
}
const_reverse_iterator crend() const {
return _intern.crend();
}
};
} // namespace std
} // namespace AGS3

View File

@ -279,7 +279,6 @@ MODULE_OBJS = \
engine/script/runtime_script_value.o \
engine/script/script.o \
engine/script/script_api.o \
engine/script/script_engine.o \
engine/script/script_runtime.o \
engine/script/system_imports.o \
plugins/ags_plugin.o \

View File

@ -42,10 +42,8 @@ int ccGetOption(int optbit) {
return 0;
}
// Returns full script error message and callstack (if possible)
extern std::pair<String, String> cc_error_at_line(const char *error_msg);
// Returns script error message without location or callstack
extern String cc_error_without_line(const char *error_msg);
// Returns current running script callstack as a human-readable text
extern String cc_get_callstack(int max_lines = INT_MAX);
void cc_clear_error() {
_GP(ccError) = ScriptError();
@ -71,15 +69,12 @@ void cc_error(const char *descr, ...) {
String displbuf = String::FromFormatV(descr, ap);
va_end(ap);
if (_G(currentline) > 0) {
// [IKM] Implementation is project-specific
std::pair<String, String> errinfo = cc_error_at_line(displbuf.GetCStr());
_GP(ccError).ErrorString = errinfo.first;
_GP(ccError).CallStack = errinfo.second;
} else {
_GP(ccError).ErrorString = cc_error_without_line(displbuf.GetCStr());
_GP(ccError).CallStack = "";
}
String callstack = cc_get_callstack();
if ((_G(currentline) > 0) && callstack.IsEmpty())
_GP(ccError).ErrorString = String::FromFormat("Error (line %d): %s", _G(currentline), displbuf.GetCStr());
else
_GP(ccError).ErrorString = String::FromFormat("Error: %s", displbuf.GetCStr());
_GP(ccError).CallStack = callstack;
_GP(ccError).HasError = 1;
_GP(ccError).Line = _G(currentline);