tinku99 wrote:
I may be able to export this function in the dll. If you reenable __ParseExpressionArg() in low level, maybe can we just pass the arg result from __ParseExpressionArg to ExpressionToPostfix() ?
That may work. You may find the old implementation of __ParseExpressionArg in
LowLevel_104706.zip.
Quote:
Also, I was able to mix your anonymous function and Call() to 1.0.48:
Well done!
Code:
eline := code_line(cg, ACT_EXPRESSION )
code_arg(cg, 0, 1)
code_line_end(cg) ; <-- add this
arg := NumGet(eline+0, 4)
Args are not attached to the line and removed from the codegen's internal buffer until the line is "finished" via code_line_end. This is done automatically, but only if and when you call code_line (to begin the next line) or code_finalize. At the time your code retrieves the Arg array, it is NULL.
Code:
var := __getvar(NumGet(param+0))
__getvar returns a pointer to a Var structure, given a variable reference like
var or
%varContainingNameOfVar%. If it is passed anything else, it returns 0. Since what NumGet returns is already a pointer to a Var structure, you simply need to remove
__getvar().
The next problem (which was causing the crash) was difficult to find until I ran the script with the Visual C++ debugger. Normally each arg is assigned text, and this text is later parsed to form the postfix array. When
ListLines is called and the dynamic line must be converted to text, AutoHotkey sees that the text is empty and assumes the arg has an associated input/output variable. Since it does not, this causes an access violation. The simplest solution is to add a
static variable
dynamic_text:="[dynamic]", and the following:
Code:
NumPut(&dynamic_text, arg+4)
The final problem was difficult to find, but an easy fix:
Code:
param := NumGet(func+_Param)
Your copy-pasting has resulted in two variables which point to the same function: func and advice_func. Since param is used to retrieve variable references to pass to the advice function (also referred to as 'func'), it essentially passes the advice function's parameters back to itself. Change
func to
advisee_func.
There are additional changes I would recommend, to clean up the code, improve performance and re-enable essential features. First, remove the following lines:
Code:
if !(func := __findFunc(advice))
return
if NumGet(func+16) > 10 ; Give up, requires too many parameters.
return
Next, replace all references to the variable
func with
advice_func, which contained the same value. Note that Call() imposed a limit of 10 parameters only because it could not accept more (without modification).
Since we've removed the limit of 10 parameters, the buffer size we set may be inadequate:
Code:
; VarSetCapacity(postfix%count%, 204, 0)
VarSetCapacity(postfix%count%, 16*(param_count+2)+12, 0)
In order to support functions with <> 2 parameters, change
Loop, 2 to
Loop, %param_count%.
To support multiple "advice connections", we need to initialize
count only once and increment it each time.
Using assume-static means two things:
- Several static variables which only need to be initialized once are initialized each time.
- All local variables are static; this may cause conflicts in the rare event that the thread is interrupted and adviseUDF() is called while a previous instance is still running.
Rather than explicitly declaring each non-static local variable, it seems easier to use explicit static declarations and __static for the dynamic static variables.
Rather than the simple
dynamic_text method described above, we can store more useful information in the arg's text - for instance, [advise myadvice before func].
Finally, if we are using AutoHotkey v1.0.48.01 or later, we can use
ListLines On/Off to make debugging
much easier. AutoHotkey_L, which has not yet caught up, will ignore the On/Off part.
Code:
adviseUDF(advice, type, advisee) ; type = before or after
{
static ACT_EXPRESSION:=3, _sizeof_FuncParam:=16, _Var_Name:=24
, _JumpToLine:=4, _Param:=8, _ParamCount:=12, _MinParams:=16, _IsBuiltIn:=49
, pn:="1,2,3,4,5,6,7,8,9,10", SYM_INVALID:=56, SYM_VAR:=3, SYM_FUNC:=55, SYM_OPERAND:=4, VAR_ALIAS:=0
, count = 0
ErrorLevel := ErrorLevel ; As of v1.0.48.02, this line is omitted from ListLines. Its
ListLines Off ; presence allows the line which called adviseUDF() to be seen.
LowLevel_init()
; Resolve functions and validate.
If !(advice_func := __findFunc(advice))
|| !(advisee_func := __findFunc(advisee))
|| NumGet(advisee_func+_IsBuiltIn,0,"char")
{
ListLines On
return false
}
param_count := NumGet(advisee_func+_ParamCount)
; Begin generating code.
cg := code_gen()
; Begin a standalone expression. Each standalone expression has only one arg.
eline := code_line(cg, ACT_EXPRESSION )
code_arg(cg, 0, 1)
code_line_end(cg)
arg := NumGet(eline+0, 4)
; postfix%count% must fit one ExprTokenType for each param, followed by a SYM_FUNC token
; representing a function call, a SYM_INVALID token terminating the expression and
; a DerefType holding a pointer to the function to call.
count += 1
__static(postfix%count%), __static(text%count%:="[advise " advice " " type " " advisee "]")
VarSetCapacity(postfix%count%, 16*(param_count+2)+12, 0) ; 16*(MAX_PARAMS+2) + 12*1
this_postfix := &postfix%count%
param := NumGet(advisee_func+_Param)
pc := 0
Loop, % param_count
{
; Ignore any parameters beyond the function's capacity.
If (pc >= NumGet(advice_func+12))
break
var := NumGet(param+0)
; Resolve ByRef/alias.
If (NumGet(var+23,0,"char") = VAR_ALIAS)
var := NumGet(var+12)
; Put param var into postfix%count% array.
NumPut(var, this_postfix+0), NumPut(SYM_VAR, this_postfix+8)
; Move to next token in postfix%count% array, increment param count.
this_postfix += 16
pc++
param += _sizeof_FuncParam
}
; Set up the DerefType at the end of the postfix%count% array.
func_deref := this_postfix+32
NumPut(advice_func, func_deref+4)
NumPut(pc, func_deref+9, 0, "char")
; Set up SYM_FUNC token.
NumPut(func_deref, this_postfix+0)
NumPut(SYM_FUNC, this_postfix+8)
this_postfix += 16
; Terminate postfix%count% array.
NumPut(SYM_INVALID, this_postfix+8)
; Set postfix%count% array of the first arg of 'return' below.
NumPut(&postfix%count%, arg+12)
NumPut(&text%count%, arg+4)
If (type == "after")
{
line1 := Numget(advisee_func+_JumpToLine, 0, "UInt")
Loop
{
lineAct := NumGet(line1+0, 0, "UChar")
If (lineAct != 102)
line1 := NumGet(line1+0, 20, "UInt")
Else
break
}
If (ch := code_finalize(cg))
{
; Insert the code before the first return statement
code_insert_before(ch, line1)
code_delete_handle(ch)
}
}
Else
{
If (ch := code_finalize(cg))
{
; Insert the code before the jump-to line of the function.
code_insert_before(ch, NumGet(advisee_func+_JumpToLine))
; Re-target the function at the new line.
NumPut(eline, advisee_func+_JumpToLine)
code_delete_handle(ch)
}
}
code_gen_delete(cg)
ListLines On
return !!ch
}
myFunc("Apple","banana")
adviseUDF("SwapParameters","before","myFunc")
myFunc("Apple","banana")
adviseUDF("EXCLAIM","before","myFunc")
adviseUDF("LogParams","before","myFunc")
myFunc("Apple","banana")
adviseUDF("LogParams","after","threeparams")
threeparams("alpha","bravo","charlie")
ListLines
Pause
threeparams(a,b,c) {
a=%a%
return
}
myFunc(A,B) {
MsgBox 0, myFunc, %A%, meet %B%.
return
}
Exclaim(ByRef A, ByRef B) {
StringUpper, A, A
StringUpper, B, B
}
SwapParameters(ByRef A, ByRef B) {
C := A
A := RegExReplace(B,"^\w","$U0")
B := RegExReplace(C,"^\w","$l0")
}
LogParams(a="", b="", c="") {
MsgBox 0, LogParams, %a%,%b%,%c%
}