AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

LowLevel & dynamic code
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
ahklerner



Joined: 26 Jun 2006
Posts: 1317
Location: USA

PostPosted: Fri Aug 28, 2009 4:14 am    Post subject: Reply with quote

Wow thanks alot for the demonstration! very good. sorry for being impatient, i just got excited using this stuff
_________________

ʞɔпɟ əɥʇ ʇɐɥʍ
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 4469
Location: Qld, Australia

PostPosted: Fri Aug 28, 2009 6:29 am    Post subject: Reply with quote

Here's another demonstration: how to insert a single line without breaking when the target is the single-line body of an if/else/loop/while. It also serves as a means to determine the active OnExit subroutine, with the limitation that the function must be called at least once before OnExit. It does this by replacing OnExit <arg> with sOnExit:=<arg> .. OnExit %sOnExit% (where <arg> may be a variable, text, expression or omitted; and braces are inserted if necessary).
Code:
ErrorLevel=0        ; Without this, insert_line_before fails for the line below.
OnExit Undetected   ; With it, insert succeeds but the inserted line never runs.
MsgBox % GetCurrentOnExitRoutine()
OnExit Foo1
MsgBox % GetCurrentOnExitRoutine()
if 0 = 0
    OnExit Foo2
MsgBox % GetCurrentOnExitRoutine()
if 0 = 0
{
    OnExit
}
MsgBox % GetCurrentOnExitRoutine()
ListLines
Pause
Foo1:
Foo2:
Undetected:
ExitApp

GetCurrentOnExitRoutine()
{
    static ACT_ONEXIT, sOnExit
    if !ACT_ONEXIT
    {   ; Initialize. Must be done before the first time OnExit is called.
        ACT_ASSIGNEXPR := 2
        ACT_ONEXIT := 97
        LowLevel_init()
        cg := code_gen()
        line_ptr := __getFirstLine()
        while line_ptr
        {
            ActionType := NumGet(line_ptr+0, 0, "UChar")
            if (ActionType = ACT_ONEXIT)
            {
                ; Create an impossible assignment line with three args.
                assn := code_line(cg, ACT_ASSIGNEXPR)
                code_arg(cg, 2, __getVar(sOnExit))  ; Output var/target of assignment.
                code_arg(cg, 0)                     ; Empty text arg, may be overwritten.
                code_arg(cg, 1, __getVar(sOnExit))  ; Extra %sOnExit% arg for OnExit.
                code_line_end(cg)
                code_gen_reset(cg, false) ; Reset cg and "detach" line.
               
                l_argc := NumGet(line_ptr+1, 0, "UChar")
                l_args := NumGet(line_ptr+4)
                a_args := NumGet(assn+4)
                if l_argc ; OnExit had an arg.
                {
                    NumPut(a_args+32, line_ptr+4)   ; line_ptr->Arg = a_args+32 (third arg)
                    ; Overwrite second arg of assn with first arg of OnExit.
                    DllCall("RtlMoveMemory", "uint", a_args+16, "uint", l_args, "uint", 16)
                }
                ; else leave OnExit with no args and assn with an empty second arg.
                NumPut(2, assn+1, 0, "UChar")       ; assn->Argc = 2
               
                if !insert_line_before(assn, line_ptr)
                    MsgBox % "Error inserting at line " NumGet(line_ptr+8) ": " ErrorLevel
            }
            line_ptr := NumGet(line_ptr+20, 0, "UInt")
        }
        code_gen_delete(cg) ; Code was already detached via code_gen_reset.
    }
    return sOnExit
}

; v1.0.48 +
; Insert a single line before target, automatically wrapping both lines in braces if required.
insert_line_before(to_insert, target)
{
    if !(prev := NumGet(target+16))
        return 0, ErrorLevel:="Bad target->PrevLine: insertion will have no effect"
    if NumGet(target+0,0,"uchar") = 9
        return 0, ErrorLevel:="Bad target->ActionType: ACT_ELSE"
    action := NumGet(to_insert+0,0,"uchar")   ; if/else/loop/while/{/} (not in that order):
    if (action >= 10 && action <= 33) || action = 104 || action = 105 || action = 108 || action = 109 || action = 8
        return 0, ErrorLevel:="Bad to_insert->ActionType"
   
    ; If the previous line is of a type which requires braces (if/else/loop/while):
    action := NumGet(prev+0,0,"uchar")
    if (action >= 10 && action <= 33) || action = 104 || action = 105 || action = 8
    {
        ; Since target->PrevLine is an if/else/loop/while and target is not {,
        ; target must be the single-line body of the line preceding it.
        bb := __lineAlloc()
        be := __lineAlloc()
        NumPut(108, bb+0, 0, "uchar")   ; bb->ActionType = ACT_BLOCK_BEGIN
        NumPut(109, be+0, 0, "uchar")   ; be->ActionType = ACT_BLOCK_END
        NumPut(NumGet(prev+24), bb+24)  ; bb->RelatedLine = prev->RelatedLine
        NumPut(prev, bb+28)             ; bb->ParentLine = prev
        NumPut(bb, target+28)           ;   target->ParentLine = bb
        NumPut(bb, be+28)               ;   be->ParentLine = bb
        NumPut(bb, prev+20)             ; prev->NextLine = bb
        NumPut(target, bb+20)           ;   bb->NextLine = target
        NumPut(NumGet(target+20), be+20)  ;   be->NextLine = target->NextLine
        NumPut(be, target+20)             ;   target->NextLine = be
        prev := bb  ; Now let below insert to_insert between bb and target.
    }
    NumPut(to_insert, prev+20) ; prev->NextLine = to_insert
    NumPut(target, to_insert+20) ; to_insert->NextLine = target
    NumPut(NumGet(target+28), to_insert+28) ; to_insert->ParentLine = target->ParentLine
    return 1, ErrorLevel:=0
}

This would be a lot easier to write, more maintainable, secure, reliable, readable, etc. etc. if we had struct support. I hope to include struct support and definitions of these structures in a future version of AutoHotkey_L. Smile
Back to top
View user's profile Send private message Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 4469
Location: Qld, Australia

PostPosted: Thu Feb 18, 2010 1:25 pm    Post subject: Reply with quote

Since AutoHotkey_L introduced objects and this required additional "symbol" types for the expression evaluator, some of the existing symbol types have different values to mainstream AutoHotkey. Call, which demonstrates dynamically calling a function, must therefore be updated to work in recent revisions of AutoHotkey_L:
Code:
        , SYM_INVALID:=56, SYM_VAR:=3, SYM_FUNC:=55, SYM_OPERAND:=4, VAR_ALIAS:=0
        , SYM_INVALID:=62, SYM_VAR:=3, SYM_FUNC:=59, SYM_OPERAND:=4, VAR_ALIAS:=0

Note that Call usually has no advantages over dynamic function calls, but there is one: the actual number of parameters passed to the function is calculated based on the parameter values. For instance, if the code needs to pass between zero and four parameters, it is tempting to do this:
Code:
fn := "MyFunc",  %fn%(prm1, prm2, prm3, prm4)
However, all parameters are passed even if some are not meaningful. So for instance, if MyFunc defines parameter 4 as optional, it will be overwritten even if prm4 has no meaningful value. Call can work around this issue:
Code:
prm1 := "one", prm2 := "two", prm3 := "three"
prm4 := "__deFault__"
Call("MyFunc", prm1, prm2, prm3, prm4)
In this case, only the first three parameters are passed. If MyFunc required four parameters, an empty string would be passed in place of "__deFault__". This behaviour can also be important for DllCall, where passing empty parameters is different from not passing parameters.
Code:
prm1 := "GetCommandLine"
prm2 := "str"
prm3 := "__deFault__"  ; prm3 and on will not be passed.
MsgBox % Call("DllCall", prm1, prm2, prm3, prm4)

This post inspired by recent discussion with majkinetor.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Page 10 of 10

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group