Permalink
Browse files

Fix Gui bug and implemeng macro function

  • Loading branch information...
HotKeyIt committed Jul 9, 2017
1 parent 1346781 commit e3e576347df486d3c5410f24a337cf353e590674
Showing with 115 additions and 8 deletions.
  1. +2 −0 source/defines.h
  2. +46 −2 source/script.cpp
  3. +9 −2 source/script.h
  4. +53 −2 source/script_expression.cpp
  5. +3 −2 source/script_gui.cpp
  6. +2 −0 source/util.cpp
View
@@ -887,6 +887,7 @@ struct global_struct
DerefType* ExcptDeref;
bool InTryBlock;
BYTE ZipCompressionLevel;
Func* CurrentMacro;
};
inline void global_maximize_interruptibility(global_struct &g)
@@ -924,6 +925,7 @@ inline void global_clear_state(global_struct &g)
g.mLoopField = NULL;
g.ThrownToken = NULL;
g.InTryBlock = false;
g.CurrentMacro = NULL;
}
inline void global_init(global_struct &g)
View
@@ -643,7 +643,7 @@ Script::~Script() // Destructor.
{
GuiType* gui;
while (gui = g_firstGui) // Destroy any remaining GUI windows (due to e.g. circular references). Also: assignment.
gui->Destroy();
gui->Destroy(false);
g_firstGui = NULL;
g_lastGui = NULL;
}
@@ -2273,6 +2273,11 @@ bool IsFunction(LPTSTR aBuf, bool *aPendingFunctionHasBrace = NULL)
// *aPendingFunctionHasBrace is set to true if a brace is present at the end, or false otherwise.
// In addition, any open-brace is removed from aBuf in this mode.
{
if (!_tcsnicmp(aBuf, _T("macro "), 6))
{
aBuf += 6;
}
LPTSTR action_end = StrChrAny(aBuf, EXPR_ALL_SYMBOLS EXPR_ILLEGAL_CHARS);
// Can't be a function definition or call without an open-parenthesis as first char found by the above.
// In addition, if action_end isn't NULL, that confirms that the string in aBuf prior to action_end contains
@@ -6211,6 +6216,8 @@ ResultType Script::ParseAndAddLine(LPTSTR aLineText, ActionTypeType aActionType
}
else if (!_tcsnicmp(aLineText, _T("Global"), 6))
{
if (g->CurrentFunc && g->CurrentFunc->mIsMacro)
return ScriptError(_T("Global variables are not supported for macros."), aLineText); // Vague error since so rare.
cp = aLineText + 6; // The character after the declaration word.
declare_type = g->CurrentFunc ? VAR_DECLARE_GLOBAL : VAR_DECLARE_SUPER_GLOBAL;
}
@@ -6223,6 +6230,8 @@ ResultType Script::ParseAndAddLine(LPTSTR aLineText, ActionTypeType aActionType
}
else if (!_tcsnicmp(aLineText, _T("Static"), 6)) // Static also implies local (for functions that default to global).
{
if (g->CurrentFunc && g->CurrentFunc->mIsMacro)
return ScriptError(_T("Static variables are not supported for macros."), aLineText); // Vague error since so rare.
cp = aLineText + 6; // The character after the declaration word.
declare_type = VAR_DECLARE_STATIC;
}
@@ -9052,6 +9061,12 @@ Func *Script::FindFunc(LPCTSTR aFuncName, size_t aFuncNameLength, int *apInsertP
if (!aFuncNameLength) // Caller didn't specify, so use the entire string.
aFuncNameLength = _tcslen(aFuncName);
if (!_tcsnicmp(aFuncName, _T("macro "), 6))
{
aFuncName += 6;
aFuncNameLength -= 6;
}
if (apInsertPos) // L27: Set default for maintainability.
*apInsertPos = -1;
@@ -9161,9 +9176,22 @@ Func *Script::AddFunc(LPCTSTR aFuncName, size_t aFuncNameLength, bool aIsBuiltIn
// Returns the address of the new function or NULL on failure.
// The caller must already have verified that this isn't a duplicate function.
{
bool aIsMacro = false;
if (!aFuncNameLength) // Caller didn't specify, so use the entire string.
aFuncNameLength = _tcslen(aFuncName);
if (!_tcsnicmp(aFuncName, _T("macro "), 6))
{
if (aClassObject)
{
ScriptError(_T("Macro is not supported for Methods."), aFuncName);
return NULL;
}
aIsMacro = true;
aFuncName += 6;
aFuncNameLength -= 6;
}
if (aFuncNameLength > MAX_VAR_NAME_LENGTH) // FindFunc(), BIF_OnMessage() and perhaps others rely on this limit being enforced.
{
ScriptError(_T("Function name too long."), aFuncName);
@@ -9225,6 +9253,9 @@ Func *Script::AddFunc(LPCTSTR aFuncName, size_t aFuncNameLength, bool aIsBuiltIn
// Also add it to the script's list of functions, to support #Warn LocalSameAsGlobal
// and automatic cleanup of objects in static vars on program exit.
}
if (aIsMacro)
the_new_func->mIsMacro = true;
if (mFuncCount == mFuncCountMax)
{
@@ -14266,7 +14297,20 @@ ResultType Line::Perform()
// x := "quoted literal string"
// x := normal_var
ASSERT(!*mArg[0].text); // Pre-resolved. Dynamic assignments are handled as ACT_EXPRESSION.
output_var = VAR(mArg[0]);
if (g.CurrentMacro)
{
bool aVarIsParam = false;
LPTSTR aVarName = VAR(mArg[0])->mName;
FuncParam *aFuncParam = g.CurrentMacro->mParam;
for (int aParamIndex = g.CurrentMacro->mParamCount; aParamIndex; aParamIndex--)
if (!_tcscmp(aVarName, aFuncParam[aParamIndex - 1].var->mName) && (aVarIsParam = true))
break;
output_var = !aVarIsParam ? g_script->FindOrAddVar(VAR(mArg[0])->mName) : VAR(mArg[0]);
}
else
{
output_var = VAR(mArg[0]);
}
// HotKeyIt override routine for manually added BuildIn variables
// if (!(mArg[1].postfix->symbol == SYM_VAR && (mArg[1].postfix->var->mType == VAR_BUILTIN)))
return output_var->Assign(*mArg[1].postfix);
View
@@ -1768,6 +1768,7 @@ class Func : public IObjectComCompatible
// override in the script. So mIsBuiltIn should always be used to determine whether the function
// is truly built-in, not its name.
bool mIsVariadic;
bool mIsMacro;
#define MAX_FUNC_OUTPUT_VAR 7
bool ArgIsOutputVar(int aIndex)
@@ -1801,7 +1802,11 @@ class Func : public IObjectComCompatible
// would not be significant because the Return command's expression (arg1) must still be evaluated
// in case it calls any functions that have side-effects, e.g. "return LogThisError()".
Func *prev_func = g->CurrentFunc; // This will be non-NULL when a function is called from inside another function.
g->CurrentFunc = this;
Func *prev_macro = g->CurrentMacro;
if (!mIsMacro)
g->CurrentFunc = this;
else
g->CurrentMacro = this;
// Although a GOTO that jumps to a position outside of the function's body could be supported,
// it seems best not to for these reasons:
// 1) The extreme rarity of a legitimate desire to intentionally do so.
@@ -1851,6 +1856,7 @@ class Func : public IObjectComCompatible
// Due to the synchronous nature of recursion and recursion-collapse, this should keep
// g->CurrentFunc accurate, even amidst the asynchronous saving and restoring of "g" itself:
g->CurrentFunc = prev_func;
g->CurrentMacro = prev_macro;
return result;
}
@@ -1876,6 +1882,7 @@ class Func : public IObjectComCompatible
, mDefaultVarType(VAR_DECLARE_NONE)
, mIsBuiltIn(aIsBuiltIn)
, mIsVariadic(false)
, mIsMacro(false)
{}
void *operator new(size_t aBytes){ return malloc(aBytes); }
void *operator new[](size_t aBytes) {return malloc(aBytes); }
@@ -2545,7 +2552,7 @@ class GuiType : public ObjectBase
Dispose();
}
void Destroy();
void Destroy(bool aExitIfNotPersistent = true);
void Dispose();
static void DestroyIconsIfUnused(HICON ahIcon, HICON ahIconSmall); // L17: Renamed function and added parameter to also handle the window's small icon.
IObject_Type_Impl("Gui")
@@ -280,6 +280,17 @@ LPTSTR Line::ExpandExpression(int aArgIndex, ResultType &aResult, ResultToken *a
this_token.marker_length = result_length;
this_token.symbol = SYM_STRING;
} // if (this_token.symbol == SYM_DYNAMIC)
else if (this_token.symbol == SYM_VAR && g->CurrentMacro)
{
bool aVarIsParam = false;
LPTSTR aVarName = this_token.var->mName;
FuncParam *aFuncParam = g->CurrentMacro->mParam;
for (int aParamIndex = g->CurrentMacro->mParamCount; aParamIndex; aParamIndex--)
if (!_tcscmp(aVarName, aFuncParam[aParamIndex - 1].var->mName) && (aVarIsParam = true))
break;
if (!aVarIsParam)
this_token.var = g_script->FindOrAddVar(this_token.var->mName);
}
goto push_this_token;
} // if (IS_OPERAND(this_token.symbol))
@@ -2205,14 +2216,40 @@ VarSizeType Line::GetExpandedArgSize(Var *aArgVar[])
// Pre-resolved output vars should never be included in the space calculation,
// but we do need to store the var reference in aArgVar for our caller.
ASSERT(!*this_arg.text);
aArgVar[i] = VAR(this_arg);
if (g->CurrentMacro)
{
bool aVarIsParam = false;
LPTSTR aVarName = VAR(mArg[0])->mName;
FuncParam *aFuncParam = g->CurrentMacro->mParam;
for (int aParamIndex = g->CurrentMacro->mParamCount; aParamIndex; aParamIndex--)
if (!_tcscmp(aVarName, aFuncParam[aParamIndex - 1].var->mName) && (aVarIsParam = true))
break;
aArgVar[i] = !aVarIsParam ? g_script->FindOrAddVar(aVarName) : VAR(this_arg);
}
else
{
aArgVar[i] = VAR(this_arg);
}
continue;
}
if (this_arg.type == ARG_TYPE_INPUT_VAR)
{
ASSERT(!*this_arg.text);
the_only_var_of_this_arg = VAR(this_arg);
if (g->CurrentMacro)
{
bool aVarIsParam = false;
LPTSTR aVarName = VAR(mArg[0])->mName;
FuncParam *aFuncParam = g->CurrentMacro->mParam;
for (int aParamIndex = g->CurrentMacro->mParamCount; aParamIndex; aParamIndex--)
if (!_tcscmp(aVarName, aFuncParam[aParamIndex - 1].var->mName) && (aVarIsParam = true))
break;
the_only_var_of_this_arg = !aVarIsParam ? g_script->FindOrAddVar(aVarName) : VAR(this_arg);
}
else
{
the_only_var_of_this_arg = VAR(this_arg);
}
aArgVar[i] = the_only_var_of_this_arg; // For now, this is done regardless of whether it must be dereferenced.
if ( !(result = ArgMustBeDereferenced(the_only_var_of_this_arg, i, aArgVar)) )
return VARSIZE_ERROR;
@@ -2274,6 +2311,20 @@ ResultType Line::ArgMustBeDereferenced(Var *aVar, int aArgIndex, Var *aArgVar[])
for (int i = 0; i < mArgc; ++i)
if (mArg[i].type == ARG_TYPE_OUTPUT_VAR) // Implies i != aArgIndex, since this function is not called for output vars.
{
if (g->CurrentMacro)
{
bool aVarIsParam = false;
LPTSTR aVarName = VAR(mArg[0])->mName;
FuncParam *aFuncParam = g->CurrentMacro->mParam;
for (int aParamIndex = g->CurrentMacro->mParamCount; aParamIndex; aParamIndex--)
if (!_tcscmp(aVarName, aFuncParam[aParamIndex - 1].var->mName) && (aVarIsParam = true))
break;
output_var = (i < aArgIndex) ? aArgVar[i] : mArg[i].is_expression ? NULL : (!aVarIsParam ? g_script->FindOrAddVar(VAR(mArg[i])->mName) : VAR(mArg[i])); // aArgVar: See top of this function for comments.
}
else
{
output_var = VAR(mArg[0]);
}
output_var = (i < aArgIndex) ? aArgVar[i] : mArg[i].is_expression ? NULL : VAR(mArg[i]); // aArgVar: See top of this function for comments.
if (!output_var) // Var hasn't been resolved yet. To be safe, we must assume deref is required.
return CONDITION_TRUE;
View
@@ -2282,7 +2282,7 @@ _thread_local HWND GuiType::sTreeWithEditInProgress = NULL;
void GuiType::Destroy()
void GuiType::Destroy(bool aExitIfNotPersistent)
// Destroys the window and performs related cleanup which is only necessary for
// a successfully constructed Gui, then calls Dispose() for the remaining cleanup.
{
@@ -2365,7 +2365,8 @@ void GuiType::Destroy()
// IT IS NOW UNSAFE TO REFER TO ANY NON-STATIC MEMBERS OF THIS OBJECT.
// If this Gui was the last thing keeping the script running, exit the script:
g_script->ExitIfNotPersistent(EXIT_DESTROY);
if (aExitIfNotPersistent)
g_script->ExitIfNotPersistent(EXIT_DESTROY);
}
void GuiType::Dispose()
View
@@ -2925,6 +2925,8 @@ ResultType LoadDllFunction(LPTSTR parameter, LPTSTR aBuf)
*(_tcschr(aFuncName, ',')) = '\0';
ltrim(parameter);
int insert_pos;
if (!*aFuncName)
return g_script->ScriptError(_T("Empty function name, make sure to use space after #DllImport not ','."), parameter); // Seems more descriptive than "Function already defined."
Func *found_func = g_script->FindFunc(aFuncName, _tcslen(aFuncName), &insert_pos);
if (found_func)
return g_script->ScriptError(_T("Duplicate function definition."), aFuncName); // Seems more descriptive than "Function already defined."

0 comments on commit e3e5763

Please sign in to comment.