feat: 一些 Bug 修复和脚本处理相关

This commit is contained in:
寂静的羽夏 2025-04-30 17:25:39 +08:00
parent d8069aedde
commit c01ca038a3
22 changed files with 1862 additions and 1460 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -25,43 +25,49 @@
AngelObjString::AngelObjString() {}
QString AngelObjString::stringToString(void *obj, asDebugger *dbg) {
QString AngelObjString::stringToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a string
QString val = *reinterpret_cast<QString *>(obj);
if (tag == 1) {
val.prepend('"').append('"');
}
return val;
}
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg) {
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg, asUINT tag) {
CScriptArray *arr = reinterpret_cast<CScriptArray *>(obj);
QString str;
QTextStream s(&str);
s << tr("(len=") << arr->GetSize() << QStringLiteral(")");
s << QStringLiteral(" [");
s << QStringLiteral("{");
for (asUINT n = 0; n < arr->GetSize(); n++) {
s << dbg->toString(arr->At(n), arr->GetElementTypeId(),
arr->GetArrayObjectType()->GetEngine());
arr->GetArrayObjectType()->GetEngine(), 1);
if (n < arr->GetSize() - 1)
s << ", ";
}
s << QStringLiteral("]");
s << QStringLiteral("}");
return str;
}
QString AngelObjString::charToString(void *obj, asDebugger *dbg) {
QString AngelObjString::charToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a char
QChar *val = reinterpret_cast<QChar *>(obj);
return QString(*val);
auto ret = QString(*val);
if (tag == 1) {
ret.prepend('\'').append('\'');
}
return ret;
}
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg,
asUINT tag) {
CScriptDictionary *dic = reinterpret_cast<CScriptDictionary *>(obj);
QString str;
@ -69,11 +75,11 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
auto engine = dic->GetEngine();
s << " {";
s << QStringLiteral("{");
asUINT n = 0;
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
it++, n++) {
s << "[" << it.GetKey() << "] = ";
s << QStringLiteral("[") << it.GetKey() << QStringLiteral("] = ");
// Get the type and address of the value
const void *val = it.GetAddressOfValue();
@ -83,17 +89,17 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
// active, the debugger will use the engine held inside it by
// default, but in an environment where there multiple engines this
// might not be the correct instance).
s << dbg->toString(const_cast<void *>(val), typeId, engine);
s << dbg->toString(const_cast<void *>(val), typeId, engine, 1);
if (n < dic->GetSize() - 1)
s << ", ";
s << QStringLiteral(", ");
}
s << "}";
s << QStringLiteral("}");
return str;
}
QString AngelObjString::colorToString(void *obj, asDebugger *dbg) {
QString AngelObjString::colorToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
auto color = reinterpret_cast<QColor *>(obj);

View File

@ -28,16 +28,15 @@ class AngelObjString : public QObject {
Q_OBJECT
public:
// for debugger use
static QString stringToString(void *obj, asDebugger *dbg);
static QString stringToString(void *obj, asDebugger *dbg, asUINT tag);
static QString arrayToString(void *obj, asDebugger *dbg);
static QString arrayToString(void *obj, asDebugger *dbg, asUINT tag);
static QString charToString(void *obj, asDebugger *dbg);
static QString charToString(void *obj, asDebugger *dbg, asUINT tag);
static QString dictionaryToString(void *obj, asDebugger *dbg);
static QString dictionaryToString(void *obj, asDebugger *dbg, asUINT tag);
static QString colorToString(void *obj, asDebugger *dbg);
static QString colorToString(void *obj, asDebugger *dbg, asUINT tag);
public:
// ==================================================

View File

@ -143,8 +143,9 @@ AppManager::AppManager(int &argc, char *argv[])
}
AppManager::~AppManager() {
InspectQtLogHelper::instance().destory();
ClangFormatManager::instance().save();
ScriptMachine::instance().deleteLater();
InspectQtLogHelper::instance().destory();
CommandHistoryManager::save(QConsoleWidget::history().strings_);
delete _w;
@ -155,7 +156,7 @@ AppManager *AppManager::instance() { return _instance; }
MainWindow *AppManager::mainWindow() const { return _w; }
uint AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
quint64 AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
void AppManager::openFile(const QString &file, bool autoDetect) {
EditorView *editor = nullptr;

View File

@ -38,7 +38,7 @@ public:
QApplication::tr("WingCloudStudio");
}
uint currentMSecsSinceEpoch();
quint64 currentMSecsSinceEpoch();
public slots:
void openFile(const QString &file, bool autoDetect = true);

View File

@ -414,6 +414,10 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
// first preprocess the code
AsPreprocesser prepc(engine);
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
prepc.setPragmaCallback([](const QByteArray &, AsPreprocesser *,
const QString &,
void *) -> int { return asSUCCESS; },
nullptr);
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
code.toUtf8());

View File

@ -16,6 +16,7 @@
*/
#include "asdebugger.h"
#include "class/appmanager.h"
#include "define.h"
#include <QApplication>
@ -78,6 +79,40 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
if (ctx == nullptr)
return;
// By default we ignore callbacks when the context is not active.
// An application might override this to for example disconnect the
// debugger as the execution finished.
if (ctx->GetState() != asEXECUTION_ACTIVE)
return;
auto now = AppManager::instance()->currentMSecsSinceEpoch();
auto timer = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_Timer));
auto mode = ScriptMachine::ConsoleMode(reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_ContextMode)));
bool timeOut = false;
if (timer < 0) {
timeOut = true;
} else {
if (mode == ScriptMachine::DefineEvaluator) {
timeOut = (now - timer) > 3000; // 3 s
} else {
timeOut = (now - timer) > 600000; // 10 min
}
}
if (timeOut) {
auto timeOut = tr("ScriptTimedOut");
ScriptMachine::MessageInfo info;
info.message = timeOut;
info.mode = mode;
info.type = ScriptMachine::MessageType::Error;
ScriptMachine::instance().outputMessage(info);
ctx->Abort();
return;
}
auto isDbg = reinterpret_cast<asPWORD>(
ctx->GetUserData(AsUserDataType::UserData_isDbg));
if (!isDbg) {
@ -107,12 +142,6 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
}
}
// By default we ignore callbacks when the context is not active.
// An application might override this to for example disconnect the
// debugger as the execution finished.
if (ctx->GetState() != asEXECUTION_ACTIVE)
return;
auto dbgContext = reinterpret_cast<ContextDbgInfo *>(ctx->GetUserData());
Q_ASSERT(dbgContext);
@ -368,7 +397,7 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
}
QString asDebugger::toString(void *value, asUINT typeId,
asIScriptEngine *engine) {
asIScriptEngine *engine, asUINT tag) {
if (value == nullptr)
return QStringLiteral("<null>");
@ -445,7 +474,8 @@ QString asDebugger::toString(void *value, asUINT typeId,
s << name /*type->GetPropertyDeclaration(n)*/
<< QStringLiteral(" = ")
<< toString(obj->GetAddressOfProperty(n),
obj->GetPropertyTypeId(n), type->GetEngine());
obj->GetPropertyTypeId(n), type->GetEngine(),
1);
}
}
}
@ -482,7 +512,7 @@ QString asDebugger::toString(void *value, asUINT typeId,
// Invoke the callback to get the string representation of
// this type
s << it.value()(value, this);
s << it.value()(value, this, tag);
} else {
// Unknown type: type + address
s << type->GetName() << '(' << value << ')';

View File

@ -84,7 +84,7 @@ public:
virtual ~asDebugger();
// Register callbacks to handle to-string conversions of application types
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg);
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg, asUINT tag);
void registerToStringCallback(const asITypeInfo *ti,
ToStringCallback callback);
@ -108,8 +108,9 @@ public:
// Line callback invoked by context
void lineCallback(asIScriptContext *ctx);
// tag = 1 : string should be printed with quotes
QString toString(void *value, asUINT typeId,
asIScriptEngine *engine = nullptr);
asIScriptEngine *engine = nullptr, asUINT tag = 0);
GCStatistic gcStatistics();

View File

@ -16,9 +16,11 @@
*/
#include "aspreprocesser.h"
#include "class/scriptmachine.h"
#include <QDir>
#include <QFileInfo>
#include <QStack>
Q_GLOBAL_STATIC_WITH_ARGS(
QStringList, DEFAULT_MARCO,
@ -36,7 +38,9 @@ AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
pragmaCallback = nullptr;
pragmaParam = nullptr;
definedWords = *DEFAULT_MARCO;
for (auto &m : *DEFAULT_MARCO) {
definedWords.insert(m, {});
}
}
AsPreprocesser::~AsPreprocesser() { void ClearAll(); }
@ -85,9 +89,10 @@ void AsPreprocesser::setPragmaCallback(PRAGMACALLBACK_t callback,
pragmaParam = userParam;
}
void AsPreprocesser::defineWord(const QString &word) {
void AsPreprocesser::defineWord(const QString &word,
const DefineValueType &value) {
if (!definedWords.contains(word)) {
definedWords.append(word);
definedWords.insert(word, value);
}
}
@ -114,7 +119,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// shouldn't be compiled
QByteArray::size_type pos = 0;
int nested = 0;
QStack<std::optional<bool>> m_condtionStack;
while (pos < modifiedScript.size()) {
asUINT len = 0;
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
@ -128,10 +134,14 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
modifiedScript.size() - pos, &len);
Q_UNUSED(t);
QByteArray token = modifiedScript.mid(pos, len);
QByteArray token = modifiedScript.sliced(pos, len);
pos += len;
if (token == "if") {
bool isIf = token == QStringLiteral("if");
bool isIfDef = token == QStringLiteral("ifdef");
bool isIfnDef = token == QStringLiteral("ifndef");
if (isIf || isIfDef || isIfnDef) {
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_WHITESPACE) {
@ -140,34 +150,87 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
modifiedScript.size() - pos, &len);
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.mid(pos, len);
if (isIfDef || isIfnDef) {
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
// Overwrite the #if directive with space characters to
// avoid compiler error
pos += len;
overwriteCode(modifiedScript, start, pos - start);
// Overwrite the directive with space characters to
// avoid compiler error
pos += len;
overwriteCode(modifiedScript, start, pos - start);
// Has this identifier been defined by the application or
// not?
if (!definedWords.contains(word)) {
// Exclude all the code until and including the #endif
pos = excludeCode(modifiedScript, pos);
// Has this identifier been defined by the application
// or not?
if ((isIfDef && !definedWords.contains(word)) ||
(isIfnDef && definedWords.contains(word))) {
// Exclude all the code until and including the
// #endif
pos = excludeCode(modifiedScript, pos);
m_condtionStack.push(false);
} else {
m_condtionStack.push(true);
}
qDebug().noquote() << modifiedScript;
}
} else {
// evalutate the string
auto npos = modifiedScript.indexOf('\n', pos);
auto codes = modifiedScript.sliced(pos, npos - pos);
overwriteCode(modifiedScript, start, npos - start);
auto &sm = ScriptMachine::instance();
bool ok = false;
auto ret = sm.evaluateDefine(codes, ok);
if (ret < 0) {
return asERROR;
} else {
nested++;
if (ok) {
m_condtionStack.push(true);
} else {
pos = excludeCode(modifiedScript, npos);
m_condtionStack.push(false);
}
}
qDebug().noquote() << modifiedScript;
}
} else if (token == "else") {
if (m_condtionStack.isEmpty()) {
// TODO
return asERROR;
} else {
overwriteCode(modifiedScript, start, pos - start);
auto opBool = m_condtionStack.top();
if (opBool) {
if (opBool.value()) {
pos = excludeCode(modifiedScript, pos);
m_condtionStack.top().reset();
}
} else {
// TODO
return asERROR;
}
}
qDebug().noquote() << modifiedScript;
} else if (token == "endif") {
// Only remove the #endif if there was a matching #if
if (nested > 0) {
if (m_condtionStack.isEmpty()) {
// TODO
return asERROR;
} else {
overwriteCode(modifiedScript, start, pos - start);
nested--;
m_condtionStack.pop();
}
qDebug().noquote() << modifiedScript;
}
} else
} else {
pos += len;
}
}
qDebug().noquote() << modifiedScript;
// Then check for pre-processor directives
pos = 0;
while (pos >= 0 && pos < modifiedScript.size()) {
@ -178,7 +241,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
pos += len;
continue;
}
QString token = modifiedScript.mid(pos, len);
QString token = modifiedScript.sliced(pos, len);
// Skip possible decorators before class and interface declarations
if (token == "shared" || token == "abstract" || token == "mixin" ||
@ -194,7 +257,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_IDENTIFIER) {
token = modifiedScript.mid(pos, len);
token = modifiedScript.sliced(pos, len);
if (token == "include") {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
@ -211,7 +274,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
modifiedScript[pos] == '\'')) {
// Get the include file
QString includefile =
modifiedScript.mid(pos + 1, len - 2);
modifiedScript.sliced(pos + 1, len - 2);
pos += len;
// Make sure the includeFile doesn't contain any
@ -255,7 +318,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
if (found) {
QString includefile =
modifiedScript.mid(pos, rpos - pos).trimmed();
modifiedScript.sliced(pos, rpos - pos)
.trimmed();
pos = rpos + 1;
@ -303,7 +367,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// Call the pragma callback
auto pragmaText =
modifiedScript.mid(start + 7, pos - start - 7);
modifiedScript.sliced(start + 7, pos - start - 7);
// Overwrite the pragma directive with space characters
// to avoid compiler error
@ -449,27 +513,32 @@ int AsPreprocesser::skipStatement(const QByteArray &modifiedScript, int pos) {
int AsPreprocesser::excludeCode(QByteArray &modifiedScript, int pos) {
asUINT len = 0;
int nested = 0;
int nested = 1;
while (pos < (int)modifiedScript.size()) {
engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (modifiedScript[pos] == '#') {
modifiedScript[pos] = ' ';
auto sharpPos = pos;
pos++;
// Is it an #if or #endif directive?
engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
QString token = modifiedScript.mid(pos, len);
overwriteCode(modifiedScript, pos, len);
QString token = modifiedScript.sliced(pos, len);
if (token == "if") {
if (token == "if" || token == "ifdef" || token == "ifndef") {
modifiedScript[sharpPos] = ' ';
overwriteCode(modifiedScript, pos, len);
nested++;
} else if (token == "endif") {
} else if (token == "endif" || token == "else") {
if (nested-- == 0) {
pos += len;
pos = sharpPos - 1;
break;
}
modifiedScript[sharpPos] = ' ';
overwriteCode(modifiedScript, pos, len);
} else {
modifiedScript[sharpPos] = ' ';
overwriteCode(modifiedScript, pos, len);
}
} else if (modifiedScript[pos] != '\n') {
overwriteCode(modifiedScript, pos, len);

View File

@ -35,6 +35,7 @@
#pragma warning(disable : 4786)
#endif
#include <QEventLoop>
#include <QMap>
#include <QSet>
#include <QVector>
@ -62,6 +63,16 @@ typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
// Helper class for loading and pre-processing script files to
// support include directives declarations
/** for macros, we support:
* * #if <conditions>
* * #else
* * #endif
* * #define <word>
* * #define <word> <string|int64|double>
* * #undef <word>
* * #ifdef <word>
* * #ifundef <word>
*/
class AsPreprocesser {
public:
explicit AsPreprocesser(asIScriptEngine *engine);
@ -73,6 +84,9 @@ public:
QByteArray script;
};
using DefineValueType =
std::variant<std::monostate, QString, qint64, double>;
public:
// Load a script section from a file on disk
// Returns 1 if the file was included
@ -93,7 +107,7 @@ public:
void setPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
// Add a pre-processor define for conditional compilation
void defineWord(const QString &word);
void defineWord(const QString &word, const DefineValueType &value = {});
// Enumerate included script sections
unsigned int sectionCount() const;
@ -124,7 +138,8 @@ protected:
QStringList includedScripts;
QStringList definedWords;
QEventLoop waitLoop;
QHash<QString, DefineValueType> definedWords;
};
#endif // ASPREPROCESSER_H

View File

@ -43,7 +43,75 @@
#include <QProcess>
#include <QScopeGuard>
ScriptMachine::~ScriptMachine() { destoryMachine(); }
class StringFormatter {
public:
static QString format(const QString &input, uint indentSize = 4) {
QString output;
int level = 0;
bool inString = false;
QChar stringDelim;
bool escape = false;
for (int i = 0; i < input.size(); ++i) {
QChar c = input[i];
if (inString) {
output += c;
if (escape) {
escape = false;
} else if (c == '\\') {
escape = true;
} else if (c == stringDelim) {
inString = false;
}
continue;
}
if (isQuote(c)) {
inString = true;
stringDelim = c;
output += c;
continue;
}
switch (c.unicode()) {
case '{':
output += QStringLiteral("{\n");
++level;
output += QString(level * indentSize, ' ');
break;
case '}':
output += QStringLiteral("\n");
--level;
output +=
QString(level * indentSize, ' ') + QStringLiteral("}");
break;
case ',':
output +=
QStringLiteral(",\n") + QString(level * indentSize, ' ');
break;
default:
if (c.isSpace()) {
// collapse multiple spaces outside strings
if (!output.isEmpty() && !output.endsWith(' ')) {
output += ' ';
}
} else {
output += c;
}
break;
}
}
return output;
}
private:
static bool isQuote(QChar c) { return c == '"' || c == '\''; }
};
bool ScriptMachine::init() {
if (isInited()) {
@ -77,6 +145,8 @@ bool ScriptMachine::configureEngine() {
return false;
}
_engine->SetDefaultAccessMask(0x1);
// we need utf8, the default is what we want
_engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, true);
_engine->SetEngineProperty(asEP_DISALLOW_EMPTY_LIST_ELEMENTS, true);
@ -155,6 +225,22 @@ bool ScriptMachine::configureEngine() {
return false;
}
r = _engine->RegisterGlobalFunction("string stringify(? &in obj)",
asMETHOD(ScriptMachine, stringify),
asCALL_THISCALL_ASGLOBAL, this);
Q_ASSERT(r >= 0);
if (r < 0) {
return false;
}
r = _engine->RegisterGlobalFunction(
"string beautify(const string &in str, uint indent = 4)",
asFUNCTION(beautify), asCALL_CDECL);
Q_ASSERT(r >= 0);
if (r < 0) {
return false;
}
r = _engine->RegisterGlobalFunction(
"int exec(string &out output, const string &in exe, "
"const string &in params = \"\", int timeout = 3000)",
@ -198,7 +284,8 @@ bool ScriptMachine::configureEngine() {
PluginSystem::instance().angelApi()->installAPI(this);
// create module for Console
_engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
auto mod = _engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x1);
return true;
}
@ -317,8 +404,21 @@ int ScriptMachine::execSystemCmd(QString &out, const QString &exe,
}
}
QString ScriptMachine::beautify(const QString &str, uint indent) {
return StringFormatter::format(str, indent);
}
QString ScriptMachine::stringify(void *ref, int typeId) {
return _debugger->toString(ref, typeId, _engine);
}
bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
bool isInDebug) {
bool isInDebug, int *retCode) {
Q_ASSERT(mode != Interactive && mode != DefineEvaluator);
if (script.isEmpty()) {
return true;
}
// Compile the script
auto mod = createModule(mode);
// script-running is not allowed in interactive mode
@ -326,13 +426,11 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
return false;
}
QScopeGuard guard([mod, mode]() {
if (mode != ConsoleMode::Interactive) {
// Before leaving, allow the engine to clean up remaining objects by
// discarding the module and doing a full garbage collection so that
// this can also be debugged if desired
mod->Discard();
}
QScopeGuard guard([mod]() {
// Before leaving, allow the engine to clean up remaining objects by
// discarding the module and doing a full garbage collection so that
// this can also be debugged if desired
mod->Discard();
});
asPWORD isDbg = 0;
@ -353,12 +451,13 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
builder.setPragmaCallback(&ScriptMachine::pragmaCallback, this);
builder.setIncludeCallback(&ScriptMachine::includeCallback, this);
_curMode = mode;
auto r = builder.loadSectionFromFile(script.toUtf8());
if (r < 0) {
// TODO
return false;
}
_curMode = mode;
r = builder.build(mod);
if (r < 0) {
MessageInfo info;
@ -414,6 +513,9 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
_ctx[mode] = ctx;
ctx->SetUserData(reinterpret_cast<void *>(
AppManager::instance()->currentMSecsSinceEpoch()),
AsUserDataType::UserData_Timer);
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
AsUserDataType::UserData_isDbg);
@ -492,9 +594,79 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
_debugger->clearBreakPoint();
emit onDebugFinished();
}
if (retCode) {
*retCode = r;
}
return r >= 0;
}
int ScriptMachine::evaluateDefine(const QString &code, bool &result) {
auto mod = createModuleIfNotExist(DefineEvaluator);
if (mod) {
asIScriptFunction *func = nullptr;
auto ccode = code;
ccode.prepend("bool f(){ return (").append(");}");
// start to compile
_curMode = DefineEvaluator;
auto cr = mod->CompileFunction(nullptr, ccode.toUtf8(), 0, 0, &func);
if (cr < 0) {
return cr;
}
// Set up a context to execute the script
// The context manager will request the context from the
// pool, which will automatically attach the debugger
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
_ctx[DefineEvaluator] = ctx;
ctx->SetUserData(reinterpret_cast<void *>(asPWORD(
AppManager::instance()->currentMSecsSinceEpoch())),
AsUserDataType::UserData_Timer);
asPWORD isDbg = 0;
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
AsUserDataType::UserData_isDbg);
mod->SetUserData(reinterpret_cast<void *>(isDbg),
AsUserDataType::UserData_isDbg);
asPWORD umode = asPWORD(DefineEvaluator);
ctx->SetUserData(reinterpret_cast<void *>(umode),
AsUserDataType::UserData_ContextMode);
ctx->SetExceptionCallback(asMETHOD(ScriptMachine, exceptionCallback),
this, asCALL_THISCALL);
// Execute the script until completion
// The script may create co-routines. These will automatically
// be managed by the context manager
while (_ctxMgr->ExecuteScripts()) {
qApp->processEvents();
}
_ctx[DefineEvaluator] = nullptr;
// Check if the main script finished normally
int r = ctx->GetState();
if (r != asEXECUTION_FINISHED) {
r = -1;
} else {
result = bool(ctx->GetReturnByte());
r = 0;
}
func->Release();
// Return the context after retrieving the return value
_ctxMgr->DoneWithContext(ctx);
_engine->GarbageCollect();
return r;
}
return asERROR;
}
void ScriptMachine::abortDbgScript() {
if (_debugger->getEngine()) {
_debugger->runDebugAction(asDebugger::ABORT);
@ -573,29 +745,51 @@ asIScriptModule *ScriptMachine::createModule(ConsoleMode mode) {
if (isModuleExists(mode)) {
return nullptr;
}
asIScriptModule *mod = nullptr;
switch (mode) {
case Interactive:
return nullptr;
mod = nullptr;
case Scripting:
return _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
mod = _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x1);
break;
case Background:
return _engine->GetModule("WINGSRV", asGM_ALWAYS_CREATE);
mod = _engine->GetModule("WINGSRV", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x1);
break;
case DefineEvaluator:
mod = _engine->GetModule("WINGDEF", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x2);
break;
}
// should not go there
return nullptr;
return mod;
}
asIScriptModule *ScriptMachine::createModuleIfNotExist(ConsoleMode mode) {
asIScriptModule *mod = nullptr;
switch (mode) {
case Interactive:
return _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
mod = _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
mod->SetAccessMask(0x1);
break;
case Scripting:
return _engine->GetModule("WINGSCRIPT", asGM_CREATE_IF_NOT_EXISTS);
mod = _engine->GetModule("WINGSCRIPT", asGM_CREATE_IF_NOT_EXISTS);
mod->SetAccessMask(0x1);
break;
case Background:
return _engine->GetModule("WINGSRV", asGM_CREATE_IF_NOT_EXISTS);
mod = _engine->GetModule("WINGSRV", asGM_CREATE_IF_NOT_EXISTS);
mod->SetAccessMask(0x1);
break;
case DefineEvaluator:
mod = _engine->GetModule("WINGDEF", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x2);
break;
}
// should not go there
return nullptr;
return mod;
}
asIScriptModule *ScriptMachine::module(ConsoleMode mode) {
@ -606,6 +800,8 @@ asIScriptModule *ScriptMachine::module(ConsoleMode mode) {
return _engine->GetModule("WINGSCRIPT", asGM_ONLY_IF_EXISTS);
case Background:
return _engine->GetModule("WINGSRV", asGM_ONLY_IF_EXISTS);
case DefineEvaluator:
return _engine->GetModule("WINGDEF", asGM_ONLY_IF_EXISTS);
}
return nullptr;
}
@ -1832,6 +2028,10 @@ asIScriptEngine *ScriptMachine::engine() const { return _engine; }
asDebugger *ScriptMachine::debugger() const { return _debugger; }
bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
if (code.isEmpty()) {
return true;
}
asIScriptModule *mod = createModuleIfNotExist(mode);
_engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, false);
@ -1841,26 +2041,30 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
asIScriptFunction *func = nullptr;
auto ret = parser.parse(ccode);
QList<QAsCodeParser::CodeSegment> ret;
if (mode != DefineEvaluator) {
ret = parser.parse(ccode);
}
// check whether there is any enum/class
if (std::find_if(ret.begin(), ret.end(),
[](const QAsCodeParser::CodeSegment &seg) {
switch (seg.type) {
case QAsCodeParser::SymbolType::Enum:
case QAsCodeParser::SymbolType::Class:
case QAsCodeParser::SymbolType::Function:
case QAsCodeParser::SymbolType::Interface:
case QAsCodeParser::SymbolType::Import:
case QAsCodeParser::SymbolType::Variable:
return false;
case QAsCodeParser::SymbolType::Invalid:
case QAsCodeParser::SymbolType::TypeDef:
case QAsCodeParser::SymbolType::FnDef:
return true;
}
return true;
}) != ret.end()) {
if (ret.isEmpty() ||
std::any_of(ret.begin(), ret.end(),
[](const QAsCodeParser::CodeSegment &seg) {
switch (seg.type) {
case QAsCodeParser::SymbolType::Enum:
case QAsCodeParser::SymbolType::Class:
case QAsCodeParser::SymbolType::Function:
case QAsCodeParser::SymbolType::Interface:
case QAsCodeParser::SymbolType::Import:
case QAsCodeParser::SymbolType::Variable:
return false;
case QAsCodeParser::SymbolType::Invalid:
case QAsCodeParser::SymbolType::TypeDef:
case QAsCodeParser::SymbolType::FnDef:
return true;
}
return true;
})) {
// ok, wrap the codes
ccode.prepend("void f(){").append("}");
// start to compile
@ -1881,6 +2085,10 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
_ctx[mode] = ctx;
ctx->SetUserData(reinterpret_cast<void *>(asPWORD(
AppManager::instance()->currentMSecsSinceEpoch())),
AsUserDataType::UserData_Timer);
asPWORD isDbg = 0;
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
AsUserDataType::UserData_isDbg);

View File

@ -36,9 +36,10 @@ private:
public:
// we have three console modes
enum ConsoleMode {
Interactive, // in a shell
Scripting, // in scripting dialog
Background // run codes from other way
Interactive, // in a shell
Scripting, // in scripting dialog
Background, // run codes from other way
DefineEvaluator // define result calculator
};
public:
@ -108,8 +109,7 @@ public:
asIScriptModule *module(ConsoleMode mode);
static ScriptMachine &instance();
virtual ~ScriptMachine();
void destoryMachine();
public:
bool init();
@ -126,6 +126,8 @@ public:
asIScriptEngine *engine() const;
void outputMessage(const MessageInfo &info);
public:
static void scriptAssert(bool b);
static void scriptAssert_X(bool b, const QString &msg);
@ -144,7 +146,9 @@ public slots:
bool executeCode(ConsoleMode mode, const QString &code);
// only scripting mode can be debugged
bool executeScript(ConsoleMode mode, const QString &script,
bool isInDebug = false);
bool isInDebug = false, int *retCode = nullptr);
int evaluateDefine(const QString &code, bool &result);
void abortDbgScript();
void abortScript(ConsoleMode mode);
@ -155,19 +159,19 @@ protected:
QString getCallStack(asIScriptContext *context);
void destoryMachine();
private:
void print(void *ref, int typeId);
QString getInput();
void outputMessage(const MessageInfo &info);
bool isType(asITypeInfo *tinfo, RegisteredType type);
static int execSystemCmd(QString &out, const QString &exe,
const QString &params, int timeout);
static QString beautify(const QString &str, uint indent);
QString stringify(void *ref, int typeId);
private:
static void messageCallback(const asSMessageInfo *msg, void *param);

View File

@ -57,7 +57,7 @@ void GotoWidget::handleLineChanged() {
ui->lineEdit->setStyleSheet(QString());
emit jumpToLine(p, isline);
} else {
ui->lineEdit->setStyleSheet(QStringLiteral("color: red;"));
ui->lineEdit->setStyleSheet(QStringLiteral("QLineEdit{color: red}"));
}
}

View File

@ -439,6 +439,12 @@ void ScriptingConsole::paste() {
}
}
bool ScriptingConsole::isTerminal() const { return _isTerminal; }
void ScriptingConsole::setIsTerminal(bool newIsTerminal) {
_isTerminal = newIsTerminal;
}
QString ScriptingConsole::currentCodes() const {
QTextCursor textCursor = this->textCursor();
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
@ -457,15 +463,18 @@ void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")), tr("Paste"),
QKeySequence(QKeySequence::Paste), this,
&ScriptingConsole::paste);
menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&ScriptingConsole::clearConsole);
menu.addSeparator();
menu.addAction(ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
ScriptMachine::instance().abortScript(
ScriptMachine::Background);
});
if (_isTerminal) {
menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&ScriptingConsole::clearConsole);
menu.addSeparator();
menu.addAction(ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
ScriptMachine::instance().abortScript(
ScriptMachine::Background);
});
}
menu.exec(event->globalPos());
}

View File

@ -40,6 +40,10 @@ signals:
public:
QString getInput();
bool isTerminal() const;
void setIsTerminal(bool newIsTerminal);
public slots:
void init();
@ -71,6 +75,7 @@ protected slots:
private:
QString _codes;
bool _isTerminal = true;
bool _isWaitingRead = false;
std::function<QString(void)> _getInputFn;
};

View File

@ -19,18 +19,47 @@ ScriptingConsoleBase::ScriptingConsoleBase(QWidget *parent)
}
void ScriptingConsoleBase::stdOut(const QString &str) {
writeStdOut(str);
dontHighlightLastLine(true);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
writeStdOut(lines.takeFirst());
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
writeStdOut(l);
dontHighlightLastLine(true);
}
}
void ScriptingConsoleBase::stdErr(const QString &str) {
writeStdErr(str);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
writeStdErr(lines.takeFirst());
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
writeStdErr(l);
dontHighlightLastLine(false);
}
}
void ScriptingConsoleBase::stdWarn(const QString &str) {
write(str, _warnCharFmt);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
write(lines.takeFirst(), _warnCharFmt);
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
write(l, _warnCharFmt);
dontHighlightLastLine(false);
}
}
void ScriptingConsoleBase::newLine() { _s << Qt::endl; }

View File

@ -16,6 +16,8 @@ enum AsUserDataType {
UserData_PluginFn,
UserData_isDbg,
UserData_ContextMode,
UserData_Timer,
UserData_TimeOut
};
}

View File

@ -607,6 +607,22 @@ ads::CDockAreaWidget *MainWindow::buildUpLogDock(ads::CDockManager *dock,
m_logbrowser->setOpenExternalLinks(true);
m_logbrowser->setUndoRedoEnabled(false);
m_logbrowser->addAction(newAction(
ICONRES("copy"), tr("Copy"), [=]() { m_logbrowser->copy(); },
QKeySequence::Copy));
auto a = new QAction(this);
a->setSeparator(true);
m_logbrowser->addAction(a);
m_logbrowser->addAction(newAction(ICONRES(QStringLiteral("log")),
tr("ExportLog"),
&MainWindow::on_exportlog));
m_logbrowser->addAction(newAction(ICONRES(QStringLiteral("clearhis")),
tr("ClearLog"), &MainWindow::on_clslog));
m_logbrowser->setContextMenuPolicy(Qt::ActionsContextMenu);
auto dw =
buildDockWidget(dock, QStringLiteral("Log"), tr("Log"), m_logbrowser);
return dock->addDockWidget(area, dw, areaw);
@ -803,9 +819,7 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
m_hashtable->setContextMenuPolicy(
Qt::ContextMenuPolicy::ActionsContextMenu);
auto a = new QAction(m_hashtable);
a->setText(tr("Copy"));
connect(a, &QAction::triggered, this, [=] {
auto a = newAction(ICONRES(QStringLiteral("copy")), tr("Copy"), [=] {
auto r = m_hashtable->currentIndex();
qApp->clipboard()->setText(
_hashModel->checkSumData(QCryptographicHash::Algorithm(r.row())));
@ -813,6 +827,9 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
tr("CopyToClipBoard"));
});
m_hashtable->addAction(a);
a = newAction(QStringLiteral("del"), tr("Clear"),
[=]() { _hashModel->clearData(); });
m_hashtable->addAction(a);
connect(m_hashtable->selectionModel(),
&QItemSelectionModel::currentRowChanged, a,
[=](const QModelIndex &current, const QModelIndex &) {

View File

@ -360,7 +360,7 @@ private:
template <typename Func>
inline QAction *newAction(const QString &title, Func &&slot,
const QKeySequence &shortcut = QKeySequence()) {
auto a = new QAction;
auto a = new QAction(this);
a->setText(title);
a->setShortcutVisibleInContextMenu(true);
a->setShortcut(shortcut);

View File

@ -583,6 +583,7 @@ ScriptingDialog::buildUpOutputShowDock(ads::CDockManager *dock,
ads::CDockAreaWidget *areaw) {
m_consoleout = new ScriptingConsole(this);
m_consoleout->setMode(ScriptingConsole::Output);
m_consoleout->setIsTerminal(false);
auto dw = buildDockWidget(dock, QStringLiteral("ConsoleOutput"),
tr("ConsoleOutput"), m_consoleout);
return dock->addDockWidget(area, dw, areaw);

View File

@ -170,7 +170,7 @@ private:
template <typename Func>
inline QAction *newAction(const QString &title, Func &&slot,
const QKeySequence &shortcut = QKeySequence()) {
auto a = new QAction;
auto a = new QAction(this);
a->setText(title);
a->setShortcutVisibleInContextMenu(true);
a->setShortcut(shortcut);