1
0
Fork 0

Merge pull request #2430 from xenocidewiki/exprfunc

New system for expression functions
This commit is contained in:
Duncan Ogilvie 2021-03-24 22:42:00 +01:00 committed by GitHub
commit c12eb532c7
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 416 additions and 98 deletions

View File

@ -158,6 +158,11 @@ bool _plugin_registerexprfunction(int pluginHandle, const char* name, int argc,
return pluginexprfuncregister(pluginHandle, name, argc, cbFunction, userdata);
}
bool _plugin_registerexprfunctionex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata)
{
return pluginexprfuncregisterex(pluginHandle, name, returnType, argTypes, argCount, cbFunction, userdata);
}
bool _plugin_unregisterexprfunction(int pluginHandle, const char* name)
{
return pluginexprfuncunregister(pluginHandle, name);

View File

@ -231,6 +231,26 @@ typedef struct
GUIMENUTYPE hMenu;
} PLUG_CB_MENUPREPARE;
typedef enum
{
ValueTypeNumber,
ValueTypeString,
ValueTypeAny
} ValueType;
typedef struct
{
const char* ptr;
bool isOwner;
} StringValue;
typedef struct
{
ValueType type;
duint number;
StringValue string;
} ExpressionValue;
//enums
typedef enum
{
@ -280,7 +300,8 @@ typedef enum
typedef void (*CBPLUGIN)(CBTYPE cbType, void* callbackInfo);
typedef bool (*CBPLUGINCOMMAND)(int argc, char** argv);
typedef void (*CBPLUGINSCRIPT)();
typedef duint(*CBPLUGINEXPRFUNCTION)(int argc, duint* argv, void* userdata);
typedef duint(*CBPLUGINEXPRFUNCTION)(int argc, const duint* argv, void* userdata);
typedef bool(*CBPLUGINEXPRFUNCTIONEX)(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
typedef FORMATRESULT(*CBPLUGINFORMATFUNCTION)(char* dest, size_t destCount, int argc, char* argv[], duint value, void* userdata);
typedef bool (*CBPLUGINPREDICATE)(void* userdata);
@ -316,6 +337,7 @@ PLUG_IMPEXP bool _plugin_menuentryremove(int pluginHandle, int hEntry);
PLUG_IMPEXP void _plugin_startscript(CBPLUGINSCRIPT cbScript);
PLUG_IMPEXP bool _plugin_waituntilpaused();
PLUG_IMPEXP bool _plugin_registerexprfunction(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
PLUG_IMPEXP bool _plugin_registerexprfunctionex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata);
PLUG_IMPEXP bool _plugin_unregisterexprfunction(int pluginHandle, const char* name);
PLUG_IMPEXP bool _plugin_unload(const char* pluginName);
PLUG_IMPEXP bool _plugin_load(const char* pluginName);

View File

@ -21,24 +21,27 @@ struct gens<0, S...>
};
template<typename T, int ...S, typename... Ts>
static T callFunc(const T* argv, T(*cbFunction)(Ts...), seq<S...>)
static duint callFunc(const T* argv, duint(*cbFunction)(Ts...), seq<S...>)
{
return cbFunction(argv[S]...);
return cbFunction(argv[S].number...);
}
template<typename... Ts>
static bool RegisterEasy(const String & name, duint(*cbFunction)(Ts...))
{
auto aliases = StringUtils::Split(name, ',');
auto tempFunc = [cbFunction](int argc, duint * argv, void* userdata)
auto tempFunc = [cbFunction](ExpressionValue * result, int argc, const ExpressionValue * argv, void* userdata) -> bool
{
return callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
result->type = ValueTypeNumber;
result->number = callFunc(argv, cbFunction, typename gens<sizeof...(Ts)>::type());
return true;
};
if(!ExpressionFunctions::Register(aliases[0], sizeof...(Ts), tempFunc))
return false;
for(size_t i = 1; i < aliases.size(); i++)
ExpressionFunctions::RegisterAlias(aliases[0], aliases[i]);
return true;
std::vector<ValueType> args(sizeof...(Ts));
for(auto & arg : args)
arg = ValueTypeNumber;
return ExpressionFunctions::Register(name, ValueTypeNumber, args, tempFunc);
}
void ExpressionFunctions::Init()
@ -144,21 +147,35 @@ void ExpressionFunctions::Init()
//Undocumented
RegisterEasy("bpgoto", bpgoto);
ExpressionFunctions::Register("streq", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strcmp, nullptr);
ExpressionFunctions::Register("strstr", ValueTypeNumber, { ValueTypeString, ValueTypeString }, Exprfunc::strstr, nullptr);
ExpressionFunctions::Register("strlen", ValueTypeNumber, { ValueTypeString }, Exprfunc::strlen, nullptr);
ExpressionFunctions::Register("utf16", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf16, nullptr);
ExpressionFunctions::Register("utf8", ValueTypeString, { ValueTypeNumber }, Exprfunc::utf8, nullptr);
ExpressionFunctions::Register("mod.fromname", ValueTypeNumber, { ValueTypeString }, Exprfunc::modbasefromname, nullptr);
}
bool ExpressionFunctions::Register(const String & name, int argc, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata)
bool ExpressionFunctions::Register(const String & name, const ValueType & returnType, const std::vector<ValueType> & argTypes, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata)
{
if(!isValidName(name))
return false;
EXCLUSIVE_ACQUIRE(LockExpressionFunctions);
if(mFunctions.count(name))
auto aliases = StringUtils::Split(name, ',');
if(!isValidName(aliases[0]))
return false;
if(mFunctions.count(aliases[0]))
return false;
Function f;
f.name = name;
f.argc = argc;
f.name = aliases[0];
f.argTypes = argTypes;
f.returnType = returnType;
f.cbFunction = cbFunction;
f.userdata = userdata;
mFunctions[name] = f;
mFunctions[aliases[0]] = f;
for(size_t i = 1; i < aliases.size(); i++)
ExpressionFunctions::RegisterAlias(aliases[0], aliases[i]);
return true;
}
@ -168,8 +185,10 @@ bool ExpressionFunctions::RegisterAlias(const String & name, const String & alia
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
if(!Register(alias, found->second.argc, found->second.cbFunction, found->second.userdata))
if(!Register(alias, found->second.returnType, found->second.argTypes, found->second.cbFunction, found->second.userdata))
return false;
found->second.aliases.push_back(alias);
return true;
}
@ -187,29 +206,35 @@ bool ExpressionFunctions::Unregister(const String & name)
return true;
}
bool ExpressionFunctions::Call(const String & name, std::vector<duint> & argv, duint & result)
bool ExpressionFunctions::Call(const String & name, ExpressionValue & result, std::vector<ExpressionValue> & argv)
{
SHARED_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
const auto & f = found->second;
if(f.argc != int(argv.size()))
if(f.argTypes.size() != int(argv.size()))
return false;
result = f.cbFunction(f.argc, argv.data(), f.userdata);
return true;
for(size_t i = 0; i < argv.size(); i++)
{
if(argv[i].type != f.argTypes[i] && f.argTypes[i] != ValueTypeAny)
return false;
}
return f.cbFunction(&result, argv.size(), argv.data(), f.userdata);
}
bool ExpressionFunctions::GetArgc(const String & name, int & argc)
bool ExpressionFunctions::GetType(const String & name, ValueType & returnType, std::vector<ValueType> & argTypes)
{
SHARED_ACQUIRE(LockExpressionFunctions);
auto found = mFunctions.find(name);
if(found == mFunctions.end())
return false;
argc = found->second.argc;
returnType = found->second.returnType;
argTypes = found->second.argTypes;
return true;
}
bool ExpressionFunctions::isValidName(const String & name)
{
if(!name.length())

View File

@ -1,25 +1,28 @@
#pragma once
#include "_global.h"
#include "_plugins.h"
#include <functional>
class ExpressionFunctions
{
public:
using CBEXPRESSIONFUNCTION = std::function<duint(int argc, duint* argv, void* userdata)>;
using CBEXPRESSIONFUNCTION = std::function<bool(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)>;
static void Init();
static bool Register(const String & name, int argc, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata = nullptr);
static bool Register(const String & name, const ValueType & returnType, const std::vector<ValueType> & argTypes, const CBEXPRESSIONFUNCTION & cbFunction, void* userdata = nullptr);
static bool RegisterAlias(const String & name, const String & alias);
static bool Unregister(const String & name);
static bool Call(const String & name, std::vector<duint> & argv, duint & result);
static bool GetArgc(const String & name, int & argc);
static bool Call(const String & name, ExpressionValue & result, std::vector<ExpressionValue> & argv);
static bool GetType(const String & name, ValueType & returnType, std::vector<ValueType> & argTypes);
private:
struct Function
{
String name;
int argc = 0;
ValueType returnType;
std::vector<ValueType> argTypes;
CBEXPRESSIONFUNCTION cbFunction;
void* userdata = nullptr;
std::vector<String> aliases;

View File

@ -3,6 +3,7 @@
#include "console.h"
#include "variable.h"
#include "expressionfunctions.h"
#include <algorithm>
ExpressionParser::Token::Associativity ExpressionParser::Token::associativity() const
{
@ -169,6 +170,11 @@ void ExpressionParser::tokenize()
size_t stateMemory = 0;
auto stateQuote = false;
auto len = mExpression.length();
auto push_token = [this, &stateQuote](char ch)
{
mCurToken.push_back(ch);
mCurTokenQuoted.push_back(stateQuote);
};
for(size_t i = 0; i < len; i++)
{
auto ch = mExpression[i];
@ -183,7 +189,7 @@ void ExpressionParser::tokenize()
case '[':
{
stateMemory++;
mCurToken.push_back(ch);
push_token(ch);
}
break;
@ -193,14 +199,14 @@ void ExpressionParser::tokenize()
stateMemory--;
else
mIsValidExpression = false;
mCurToken.push_back(ch);
push_token(ch);
}
break;
default:
{
if(stateMemory || stateQuote)
mCurToken.push_back(ch);
push_token(ch);
else
{
switch(ch)
@ -341,7 +347,7 @@ void ExpressionParser::tokenize()
case '\t': //ignore tabs
break;
default:
mCurToken.push_back(ch);
push_token(ch);
break;
}
}
@ -350,28 +356,39 @@ void ExpressionParser::tokenize()
}
}
if(mCurToken.length() != 0) //make sure the last token is added
mTokens.push_back(Token(mCurToken, Token::Type::Data));
{
mTokens.push_back(Token(mCurToken, resolveQuotedData()));
mCurToken.clear();
mCurTokenQuoted.clear();
}
}
void ExpressionParser::addOperatorToken(const String & data, Token::Type type)
{
if(mCurToken.length()) //add a new data token when there is data in the buffer
{
mTokens.push_back(Token(mCurToken, type == Token::Type::OpenBracket ? Token::Type::Function : Token::Type::Data));
mTokens.push_back(Token(mCurToken, type == Token::Type::OpenBracket ? Token::Type::Function : resolveQuotedData()));
mCurToken.clear();
mCurTokenQuoted.clear();
}
mTokens.push_back(Token(data, type)); //add the operator token
}
ExpressionParser::Token::Type ExpressionParser::resolveQuotedData() const
{
auto allQuoted = std::find(mCurTokenQuoted.begin(), mCurTokenQuoted.end(), false) == mCurTokenQuoted.end();
return allQuoted ? Token::Type::QuotedData : Token::Type::Data;
}
bool ExpressionParser::isUnaryOperator() const
{
if(mCurToken.length()) //data before the operator means it is no unary operator
return false;
if(!mTokens.size()) //no tokens before the operator means it is an unary operator
return true;
auto lastType = mTokens[mTokens.size() - 1].type();
auto lastType = mTokens.back().type();
//if the previous token is not data or a close bracket, this operator is a unary operator
return lastType != Token::Type::Data && lastType != Token::Type::CloseBracket;
return lastType != Token::Type::Data && lastType != Token::Type::QuotedData && lastType != Token::Type::CloseBracket;
}
void ExpressionParser::shuntingYard()
@ -389,6 +406,7 @@ void ExpressionParser::shuntingYard()
switch(token.type())
{
case Token::Type::Data: //If the token is a number, then push it to the output queue.
case Token::Type::QuotedData:
queue.push_back(token);
break;
case Token::Type::Function: //If the token is a function token, then push it onto the stack.
@ -402,7 +420,7 @@ void ExpressionParser::shuntingYard()
mIsValidExpression = false;
return;
}
const auto & curToken = stack[stack.size() - 1];
const auto & curToken = stack.back();
if(curToken.type() == Token::Type::OpenBracket)
break;
queue.push_back(curToken);
@ -421,7 +439,7 @@ void ExpressionParser::shuntingYard()
mIsValidExpression = false;
return;
}
auto curToken = stack[stack.size() - 1];
auto curToken = stack.back();
stack.pop_back(); //Pop the left parenthesis from the stack, but not onto the output queue.
if(curToken.type() == Token::Type::OpenBracket) //the bracket is already popped here
break;
@ -439,7 +457,7 @@ void ExpressionParser::shuntingYard()
const auto & o1 = token;
while(!stack.empty()) //while there is an operator token o2, at the top of the operator stack and either
{
const auto & o2 = stack[stack.size() - 1];
const auto & o2 = stack.back();
if(o2.isOperator() &&
(o1.associativity() == Token::Associativity::LeftToRight && o1.precedence() >= o2.precedence()) || //o1 is left-associative and its precedence is less than or equal to that of o2, or
(o1.associativity() == Token::Associativity::RightToLeft && o1.precedence() > o2.precedence())) //o1 is right associative, and has precedence less than that of o2,
@ -457,7 +475,7 @@ void ExpressionParser::shuntingYard()
//When there are no more tokens to read:
while(!stack.empty()) //While there are still operator tokens in the stack:
{
const auto & curToken = stack[stack.size() - 1];
const auto & curToken = stack.back();
if(curToken.type() == Token::Type::OpenBracket || curToken.type() == Token::Type::CloseBracket) //If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses.
{
mIsValidExpression = false;
@ -753,6 +771,8 @@ static bool evalOperation(ExpressionParser::Token::Type type, const ExpressionPa
default:
{
duint op1v, op2v;
if(op1.isString || op2.isString)
return false;
if(!op1.DoEvaluate(op1v, silent, baseonly) || !op2.DoEvaluate(op2v, silent, baseonly))
return false;
T resultv;
@ -802,9 +822,9 @@ bool ExpressionParser::Calculate(duint & value, bool signedcalc, bool allowassig
case Token::Type::OperatorPrefixDec:
case Token::Type::OperatorSuffixInc:
case Token::Type::OperatorSuffixDec:
if(stack.size() < 1)
if(stack.empty())
return false;
op1 = stack[stack.size() - 1];
op1 = stack.back();
stack.pop_back();
if(signedcalc)
operationSuccess = signedOperation(type, op1, op2, result, silent, baseonly, allowassign);
@ -852,9 +872,9 @@ bool ExpressionParser::Calculate(duint & value, bool signedcalc, bool allowassig
case Token::Type::OperatorAssignOr:
if(stack.size() < 2)
return false;
op2 = stack[stack.size() - 1];
op2 = stack.back();
stack.pop_back();
op1 = stack[stack.size() - 1];
op1 = stack.back();
stack.pop_back();
if(signedcalc)
operationSuccess = signedOperation(type, op1, op2, result, silent, baseonly, allowassign);
@ -873,30 +893,93 @@ bool ExpressionParser::Calculate(duint & value, bool signedcalc, bool allowassig
else if(token.type() == Token::Type::Function)
{
const auto & name = token.data();
int argc;
if(!ExpressionFunctions::GetArgc(name, argc))
ValueType returnType;
std::vector<ValueType> argTypes;
if(!ExpressionFunctions::GetType(name, returnType, argTypes))
return false;
if(int(stack.size()) < argc)
if(int(stack.size()) < argTypes.size())
return false;
std::vector<duint> argv;
argv.resize(argc);
for(auto i = 0; i < argc; i++)
std::vector<ExpressionValue> argv;
argv.resize(argTypes.size());
for(auto i = 0; i < argTypes.size(); i++)
{
duint arg;
if(!stack[stack.size() - 1].DoEvaluate(arg, silent, baseonly))
auto & top = stack[stack.size() - i - 1];
ExpressionValue arg;
if(top.isString)
{
arg = { ValueTypeString, 0, StringValue{ top.data.c_str(), false } };
}
else if(top.evaluated)
{
arg = { ValueTypeNumber, top.value };
}
else
{
duint result;
if(!top.DoEvaluate(result, silent, baseonly, value_size, isvar, hexonly))
return false;
arg = { ValueTypeNumber, result };
}
if(arg.type != argTypes[i] && argTypes[i] != ValueTypeAny)
{
if(!silent)
{
auto typeName = [](ValueType t) -> String
{
switch(t)
{
case ValueTypeNumber:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "number"));
case ValueTypeString:
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "string"));
}
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "invalid"));
};
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression function %s argument %d type mismatch (expected %s, got %s)!\n"),
name.c_str(),
argTypes.size() - i,
typeName(argTypes[i]).c_str(),
typeName(arg.type).c_str()
);
}
return false;
stack.pop_back();
argv[argc - i - 1] = arg;
}
argv[argTypes.size() - i - 1] = arg;
}
duint result;
if(!ExpressionFunctions::Call(name, argv, result))
ExpressionValue result = { ValueTypeNumber, 0 };
if(!ExpressionFunctions::Call(name, result, argv))
return false;
stack.push_back(EvalValue(result));
if(result.type == ValueTypeAny)
return false;
for(size_t i = 0; i < argv.size(); i++)
{
stack.pop_back();
}
if(result.type == ValueTypeString)
{
stack.push_back(EvalValue(result.string.ptr, true));
if(result.string.isOwner)
BridgeFree((void*)result.string.ptr);
}
else
stack.push_back(EvalValue(result.number));
}
else
stack.push_back(EvalValue(token.data()));
stack.push_back(EvalValue(token.data(), token.type() == Token::Type::QuotedData));
}
if(stack.size() != 1) //there should only be one value left on the stack
return false;
return stack[stack.size() - 1].DoEvaluate(value, silent, baseonly, value_size, isvar, hexonly);
auto top = stack.back();
if(top.isString)
{
if(!silent)
dprintf(QT_TRANSLATE_NOOP("DBG", "Expression evaluated to a string: \"%s\"\n"), StringUtils::Escape(top.data).c_str());
return false;
}
return top.DoEvaluate(value, silent, baseonly, value_size, isvar, hexonly);
}

View File

@ -27,6 +27,7 @@ public:
{
Error,
Data,
QuotedData,
Function,
Comma,
OpenBracket,
@ -112,15 +113,16 @@ public:
struct EvalValue
{
bool evaluated;
bool evaluated = false;
bool isString = false;
duint value = 0;
String data;
explicit EvalValue(duint value)
: evaluated(true), value(value) {}
explicit EvalValue(const String & data)
: evaluated(false), data(data) {}
explicit EvalValue(const String & data, bool isString)
: evaluated(false), data(data), isString(isString) {}
bool DoEvaluate(duint & result, bool silent = true, bool baseonly = false, int* value_size = nullptr, bool* isvar = nullptr, bool* hexonly = nullptr) const
{
@ -141,6 +143,7 @@ public:
private:
static String fixClosingBrackets(const String & expression);
Token::Type resolveQuotedData() const;
bool isUnaryOperator() const;
void tokenize();
void shuntingYard();
@ -168,6 +171,7 @@ private:
std::vector<Token> mTokens;
std::vector<Token> mPrefixTokens;
String mCurToken;
std::vector<bool> mCurTokenQuoted;
};
#endif //_EXPRESSION_PARSER_H

View File

@ -11,6 +11,7 @@
#include "value.h"
#include "TraceRecord.h"
#include "exhandlerinfo.h"
#include <vector>
namespace Exprfunc
{
@ -186,7 +187,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return info.branch && !info.call && !strstr(info.instruction, "jmp");
return info.branch && !info.call && !::strstr(info.instruction, "jmp");
}
duint disisbranch(duint addr)
@ -202,7 +203,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return strstr(info.instruction, "ret") != nullptr;
return ::strstr(info.instruction, "ret") != nullptr;
}
duint disiscall(duint addr)
@ -276,7 +277,7 @@ namespace Exprfunc
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(addr, &info, true))
return 0;
return info.branch && !strstr(info.instruction, "jmp") ? addr + info.size : 0;
return info.branch && !::strstr(info.instruction, "jmp") ? addr + info.size : 0;
}
duint disnext(duint addr)
@ -476,4 +477,112 @@ namespace Exprfunc
return 0;
return getLastExceptionInfo().ExceptionRecord.ExceptionInformation[index];
}
bool utf16(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
if(argc > 1 || !argc)
return false;
assert(argv[0].type == ValueTypeNumber);
duint addr = argv[0].number;
std::vector<wchar_t> tempStr(MAX_STRING_SIZE + 1);
if(!DbgMemRead(addr, tempStr.data(), sizeof(wchar_t) * (tempStr.size() - 1)))
{
return false;
}
auto utf8Str = StringUtils::Utf16ToUtf8(tempStr.data());
if(utf8Str.empty() && wcslen(tempStr.data()) > 0)
{
return false;
}
auto strBuf = BridgeAlloc(utf8Str.size() + 1);
memcpy(strBuf, utf8Str.c_str(), utf8Str.size());
result->type = ValueTypeString;
result->string.ptr = (const char*)strBuf;
result->string.isOwner = true;
return true;
}
bool utf8(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
if(argc > 1 || !argc)
return false;
assert(argv[0].type == ValueTypeNumber);
duint addr = argv[0].number;
std::vector<char> tempStr(MAX_STRING_SIZE + 1);
if(!DbgMemRead(addr, tempStr.data(), tempStr.size() - 1))
{
return false;
}
auto strlen = ::strlen(tempStr.data());
auto strBuf = BridgeAlloc(strlen + 1);
memcpy(strBuf, tempStr.data(), strlen + 1);
result->type = ValueTypeString;
result->string.ptr = (const char*)strBuf;
result->string.isOwner = true;
return true;
}
bool modbasefromname(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
result->type = ValueTypeNumber;
result->number = ModBaseFromName(argv[0].string.ptr);
if(!result->number)
return false;
return true;
}
bool strcmp(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argv[0].type == ValueTypeString);
result->type = ValueTypeNumber;
result->number = 0;
if(argc > 2 || argc <= 1)
return false;
result->number = !::strcmp(argv[0].string.ptr, argv[1].string.ptr);
return true;
}
bool strstr(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argv[0].type == ValueTypeString);
result->type = ValueTypeNumber;
result->number = 0;
if(argc > 2 || argc <= 1)
return false;
result->number = ::strstr(argv[0].string.ptr, argv[1].string.ptr) != nullptr;
return true;
}
bool strlen(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
assert(argv[0].type == ValueTypeString);
result->type = ValueTypeNumber;
result->number = 0;
if(argc != 1)
return false;
result->number = ::strlen(argv[0].string.ptr);
return true;
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "_global.h"
#include "expressionfunctions.h"
namespace Exprfunc
{
@ -80,4 +81,11 @@ namespace Exprfunc
duint exflags();
duint exinfocount();
duint exinfo(duint index);
bool strcmp(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool strstr(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool strlen(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool utf16(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool utf8(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
bool modbasefromname(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata);
}

View File

@ -983,6 +983,26 @@ bool pluginmenuentryremove(int pluginHandle, int hEntry)
return false;
}
struct ExprFuncWrapper
{
void* user;
int argc;
CBPLUGINEXPRFUNCTION cbFunc;
std::vector<duint> cbArgv;
static bool callback(ExpressionValue* result, int argc, const ExpressionValue* argv, void* userdata)
{
auto cbUser = reinterpret_cast<ExprFuncWrapper*>(userdata);
for(auto i = 0; i < argc; i++)
cbUser->cbArgv.push_back(argv[i].number);
result->type = ValueTypeNumber;
result->number = cbUser->cbFunc(argc, cbUser->cbArgv.data(), cbUser->user);
return true;
}
};
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata)
{
String plugName;
@ -991,7 +1011,18 @@ bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUG
PLUG_EXPRFUNCTION plugExprfunction;
plugExprfunction.pluginHandle = pluginHandle;
strcpy_s(plugExprfunction.name, name);
if(!ExpressionFunctions::Register(name, argc, cbFunction, userdata))
ExprFuncWrapper* wrapper = new ExprFuncWrapper;
wrapper->argc = argc;
wrapper->cbFunc = cbFunction;
wrapper->user = userdata;
std::vector<ValueType> args(argc);
for(auto & arg : args)
arg = ValueTypeNumber;
if(!ExpressionFunctions::Register(name, ValueTypeNumber, args, wrapper->callback, wrapper))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" failed to register...\n"), plugName.c_str(), name);
return false;
@ -1003,6 +1034,33 @@ bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUG
return true;
}
bool pluginexprfuncregisterex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata)
{
String plugName;
if(!findPluginName(pluginHandle, plugName))
return false;
PLUG_EXPRFUNCTION plugExprfunction;
plugExprfunction.pluginHandle = pluginHandle;
strcpy_s(plugExprfunction.name, name);
std::vector<ValueType> argTypesVec(argCount);
for(auto i = 0; i < argCount; i++)
argTypesVec[i] = argTypes[i];
if(!ExpressionFunctions::Register(name, returnType, argTypesVec, cbFunction, userdata))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" failed to register...\n"), plugName.c_str(), name);
return false;
}
EXCLUSIVE_ACQUIRE(LockPluginExprfunctionList);
pluginExprfunctionList.push_back(plugExprfunction);
EXCLUSIVE_RELEASE();
dprintf(QT_TRANSLATE_NOOP("DBG", "[PLUGIN, %s] Expression function \"%s\" registered!\n"), plugName.c_str(), name);
return true;
}
bool pluginexprfuncunregister(int pluginHandle, const char* name)
{
String plugName;

View File

@ -98,6 +98,7 @@ void pluginmenuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey);
bool pluginmenuremove(int hMenu);
bool pluginmenuentryremove(int pluginHandle, int hEntry);
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
bool pluginexprfuncregisterex(int pluginHandle, const char* name, const ValueType & returnType, const ValueType* argTypes, size_t argCount, CBPLUGINEXPRFUNCTIONEX cbFunction, void* userdata);
bool pluginexprfuncunregister(int pluginHandle, const char* name);
bool pluginformatfuncregister(int pluginHandle, const char* type, CBPLUGINFORMATFUNCTION cbFunction, void* userdata);
bool pluginformatfuncunregister(int pluginHandle, const char* type);

View File

@ -6,7 +6,7 @@
#include "disasm_helper.h"
#include "formatfunctions.h"
enum class ValueType
enum class StringValueType
{
Unknown,
SignedDecimal,
@ -19,7 +19,7 @@ enum class ValueType
Instruction
};
static String printValue(FormatValueType value, ValueType type)
static String printValue(FormatValueType value, StringValueType type)
{
duint valuint = 0;
char string[MAX_STRING_SIZE] = "";
@ -28,37 +28,37 @@ static String printValue(FormatValueType value, ValueType type)
{
switch(type)
{
case ValueType::Unknown:
case StringValueType::Unknown:
break;
#ifdef _WIN64
case ValueType::SignedDecimal:
case StringValueType::SignedDecimal:
result = StringUtils::sprintf("%lld", valuint);
break;
case ValueType::UnsignedDecimal:
case StringValueType::UnsignedDecimal:
result = StringUtils::sprintf("%llu", valuint);
break;
case ValueType::Hex:
case StringValueType::Hex:
result = StringUtils::sprintf("%llX", valuint);
break;
#else //x86
case ValueType::SignedDecimal:
case StringValueType::SignedDecimal:
result = StringUtils::sprintf("%d", valuint);
break;
case ValueType::UnsignedDecimal:
case StringValueType::UnsignedDecimal:
result = StringUtils::sprintf("%u", valuint);
break;
case ValueType::Hex:
case StringValueType::Hex:
result = StringUtils::sprintf("%X", valuint);
break;
#endif //_WIN64
case ValueType::Pointer:
case StringValueType::Pointer:
result = StringUtils::sprintf("%p", valuint);
break;
case ValueType::String:
case StringValueType::String:
if(disasmgetstringatwrapper(valuint, string, false))
result = string;
break;
case ValueType::AddrInfo:
case StringValueType::AddrInfo:
{
auto symbolic = SymGetSymbolicName(valuint);
if(disasmgetstringatwrapper(valuint, string, false))
@ -69,14 +69,14 @@ static String printValue(FormatValueType value, ValueType type)
result.clear();
}
break;
case ValueType::Module:
case StringValueType::Module:
{
char mod[MAX_MODULE_SIZE] = "";
ModNameFromAddr(valuint, mod, true);
result = mod;
}
break;
case ValueType::Instruction:
case StringValueType::Instruction:
{
BASIC_INSTRUCTION_INFO info;
if(!disasmfast(valuint, &info, true))
@ -92,33 +92,33 @@ static String printValue(FormatValueType value, ValueType type)
return result;
}
static bool typeFromCh(char ch, ValueType & type)
static bool typeFromCh(char ch, StringValueType & type)
{
switch(ch)
{
case 'd':
type = ValueType::SignedDecimal;
type = StringValueType::SignedDecimal;
break;
case 'u':
type = ValueType::UnsignedDecimal;
type = StringValueType::UnsignedDecimal;
break;
case 'p':
type = ValueType::Pointer;
type = StringValueType::Pointer;
break;
case 's':
type = ValueType::String;
type = StringValueType::String;
break;
case 'x':
type = ValueType::Hex;
type = StringValueType::Hex;
break;
case 'a':
type = ValueType::AddrInfo;
type = StringValueType::AddrInfo;
break;
case 'm':
type = ValueType::Module;
type = StringValueType::Module;
break;
case 'i':
type = ValueType::Instruction;
type = StringValueType::Instruction;
break;
default: //invalid format
return false;
@ -126,10 +126,10 @@ static bool typeFromCh(char ch, ValueType & type)
return true;
}
static const char* getArgExpressionType(const String & formatString, ValueType & type, String & complexArgs)
static const char* getArgExpressionType(const String & formatString, StringValueType & type, String & complexArgs)
{
size_t toSkip = 0;
type = ValueType::Hex;
type = StringValueType::Hex;
complexArgs.clear();
if(formatString.size() > 2 && !isdigit(formatString[0]) && formatString[1] == ':') //simple type
{
@ -152,21 +152,21 @@ static const char* getArgExpressionType(const String & formatString, ValueType &
return formatString.c_str() + toSkip;
}
static unsigned int getArgNumType(const String & formatString, ValueType & type)
static unsigned int getArgNumType(const String & formatString, StringValueType & type)
{
String complexArgs;
auto expression = getArgExpressionType(formatString, type, complexArgs);
unsigned int argnum = 0;
if(!expression || sscanf_s(expression, "%u", &argnum) != 1)
type = ValueType::Unknown;
type = StringValueType::Unknown;
return argnum;
}
static String handleFormatString(const String & formatString, const FormatValueVector & values)
{
auto type = ValueType::Unknown;
auto type = StringValueType::Unknown;
auto argnum = getArgNumType(formatString, type);
if(type != ValueType::Unknown && argnum < values.size())
if(type != StringValueType::Unknown && argnum < values.size())
return printValue(values.at(argnum), type);
return GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "[Formatting Error]"));
}
@ -234,7 +234,7 @@ static String printComplexValue(FormatValueType value, const String & complexArg
static String handleFormatStringInline(const String & formatString)
{
auto type = ValueType::Unknown;
auto type = StringValueType::Unknown;
String complexArgs;
auto value = getArgExpressionType(formatString, type, complexArgs);
if(!complexArgs.empty())