Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

LowLevel & dynamic code


  • Please log in to reply
173 replies to this topic
Lexikos
  • Administrators
  • 9445 posts
  • Last active:
  • Joined: 17 Oct 2006

This script is not being actively maintained and therefore may not work correctly on current versions of AutoHotkey. AutoHotkey.dll implements some or all of its functionality.


LowLevel.ahk contains a collection of advanced functions for AutoHotkey scripts. The base functions allow a script to access the so-called "low-level" data structures which form it. Additional functions provide other functionality by accessing these data structures. See the included help file for a list of functions.

code.ahk provides functions to simplify generating code while the script is running. For a basic overview, see Generating Code in the included help file.

Requirements: A compatible version of AutoHotkey (depends on the version of LowLevel downloaded).

Known Issues:
[*:aba3czx2]User-defined functions are only accessible if referenced in script or defined after a function which is referenced in script.
[*:aba3czx2]Since 'AND' and 'OR' are normally replaced at load-time with '&&' or '||', they are not supported by __expr or __ParseExpressionArg. Use '&&' and '||' instead.

AutoHotkey v1.0.47.06:
[*:aba3czx2]Double-derefs in __expr always resolve to a global variable.
[*:aba3czx2]If a local variable is used as the base of an array for an array-creating function such as RegExMatch, the individual array variables are created local to __expr_sub - i.e. inaccessible to the script.

AutoHotkey v1.0.48:
[*:aba3czx2]Dynamic expressions are not supported. AutoHotkey requires expressions to be fully parsed and converted to a postfix-order array of tokens before execution. It is technically feasible but entirely impractical to form an expression using VarSetCapacity and NumPut or similar while referring to the LowLevel documentation and/or AutoHotkey source code.
[*:aba3czx2]All enum_act values listed after ACT_LOOP in the LowLevel documentation are off by one since the introduction of ACT_WHILE (105).

AutoHotkey_L Revision 14+
[*:aba3czx2]See v1.0.48 above.

AutoHotkey_L Revision 28+
[*:aba3czx2]Some functions of LowLevel.ahk and code.ahk are not supported. Use at your own risk.Downloads
LowLevel for AutoHotkey v1.0.47.06
LowLevel for AutoHotkey v1.0.48
Covered by Lexikos' default copyright license.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Oct 02 2013 02:33 PM
  • Joined: 24 May 2006
:shock: :shock: :shock: :shock:

You are crazy.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Oct 02 2013 02:33 PM
  • Joined: 24 May 2006
This acctually works
#singleinstance, force
	Gui, Add, Text, Enter AHK code
	Gui, Add, Edit, w300 vCode
	Gui, Add, Button, Default gOnButton, Execute
	Gui, Add, Text, vRes x+50 w200
	Gui, Show, autosize
	__init()
return

OnButton:
	Gui, Submit, NoHide	
	res := __expr(code)
	GuiControl, , res, %res%
return


This expression doesn't work though:  1 ? 5 : 6
Hello(){
  return "Hi there"
}

Msg() {
  MsgBox Hi there
}

#include lowlevel.ahk


derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
@lexikos
:D :D :D :D :D
This is ...... actually i can't find words for it.

Let's call it great. Fantastic. Awesome.
Im still shaking my head, being so impressed.

btw i tried majkinetors lil example and it works fine (Version 1.0.47.5)

When you're still looking for a name, why not naming it:
AhkLowLevelAccess (ALLA)
AccessLowLevel (ALL)
Access_AhkLowLevel (A_ALL)

great job and many thanks for sharing it
derRaphael

Lexikos
  • Administrators
  • 9445 posts
  • Last active:
  • Joined: 17 Oct 2006

This expression doesn't work though: 1 ? 5 : 6

Works for me.
MsgBox % __expr("1 ? 5 : 6")
I did, however, find and fix two bugs in function deref parsing. "func0()+1" would not work when func0 has no parameters because the ')' was skipped and "+1" was seen as a parameter. "func0()" worked because after the ')' is nothing. "func1(func0())" would not work because the inner ')' was skipped and the outer ')' was miscounted as a parameter. :x

Btw, __expr doesn't do any expression evaluation. It constructs a Line and ArgStruct, and creates a DerefType for each var or func reference. It then assigns the Line to the __expr_sub function, and calls it.

Basically, it emulates the load-time processing of AutoHotkey.

If no var or func references are needed, it can be considerably simpler. The following is the third iteration of __expr (where the one in lowlevel.ahk is the fourth):
MsgBox % __expr("1 ? 5 : 6")

__expr(expr)
{
    static line, arg
    if !VarSetCapacity(line) {
        VarSetCapacity(line,32,0), VarSetCapacity(arg,12,0)
        NumPut(102,line,0,"char")           ; line.mActionType = ACT_RETURN
        NumPut(&arg,line,4)                 ; line.mArg = &arg
        ; Give mNextLine and mPrevLine non-NULL values in case the expression
        ; somehow causes a critical error, in which case AutoHotkey may try
        ; to use these to display the error line and the lines around it.
        NumPut(&line,NumPut(&line,line,16)) ; line.mNextLine = line.mPrevLine = &line
        NumPut(1,arg,1,"char")              ; arg.is_expression = true
        cb:=RegisterCallback("__expr_last")
        NumPut(&line,NumGet(cb+28),4)       ; cb->Func->mJumpToLine = &line
        DllCall("GlobalFree","uint",cb)
    }
    NumPut(1,line,1,"char")                 ; line.mArgc = 1
    NumPut(StrLen(expr)+1,arg,2,"short")    ; arg.length = StrLen(expr)+1
    NumPut(&expr,arg,4)                     ; arg.text = &expr
    ret := __expr_last()
    ; Reset mArgc to 0 so ListLines does not try to read 'expr', which will
    ; become invalid once __expr() returns.
    NumPut(0,line,1,"char")                 ; mArgc = 0
    return ret
}
__expr_last() {
}

I will eventually write a function to evaluate commands. (This is why I wrote __MakeExpressionArg separate from __expr.) The command names must be translated to ActionType codes, and derefs in non-expression args must also be parsed.

Once that's done, a little extra work must be done to link the lines together properly and form a working dynamic script (with If, Loop, etc.)

You are crazy.

You noticed? :lol: I lost much sleep in the aftermath of high-speed idea collision. :shock:

@lexikos
:D :D :D :D :D
This is ...... actually i can't find words for it.

Let's call it great. Fantastic. Awesome.
Im still shaking my head, being so impressed.

I was in shock for a while after coming up with "the idea." :lol:

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Oct 02 2013 02:33 PM
  • Joined: 24 May 2006
This accutally allows us to read internal function variables as this example shows (and so to change it, without calling the function):

ppFunc := __findFunc("Module_Globals")
ppvar  := __findVar("var1", ppFunc, 1)

mContents := NumGet(ppVar+0)

buf := "some new text"

DllCall("lstrcpyn", "uint", mContents, "uint", &buf, "int", StrLen(buf)+1)

Msgbox % __str( mContents , -1 )

return



Module_Globals(){
	static var1 := "this is some internal function text"
}

#include lowlevel.ahk


majkinetor
  • Moderators
  • 4512 posts
  • Last active: Oct 02 2013 02:33 PM
  • Joined: 24 May 2006
I just benchmarked in optimal case, and it doesn't provide benefits comparing to using function call to return variable result. Check it out:

ppFunc := __findFunc("Module_Globals")
ppvar  := __findVar("var1", ppFunc, 1)
mContents := NumGet(ppVar+0)
hKernal :=  DllCall("LoadLibrary", "str", "Kernel32.dll")
pMulDiv :=  DllCall("GetProcAddress", "Uint", hKernal , "uint", 612)

t := A_TickCount

loop, 100000
	s := Module_Globals("var1")
t0 := A_tickCount - t

t := A_TickCount
loop, 100000
	s := DllCall(pMulDiv,"uint",mContents,"int",1,"int",1,"str")
t1 := A_tickCount - t

MsgBox %t0% %t1%

return

Module_Globals(name){
	static var1 := "this is some internal function text", var2 := 123
	val := %name%
	return val
}

#include lowlevel.ahk

Neverthe less, static local arrays are valuable thing.

Enough with that, I am impatiently waiting for full dynamic code. That will open metaprogramming dimension in AHK.

The only bad thing about this is that it is 100% AHK version dependent.
Perhaps Chris can use your work to implement dynamic code internaly...

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Amazing! We have been waiting for these for years! Now dynamic expression evaluation works from compiled scripts, and without temporary files. :D

Ternary operators don’t work for me, either (return blank) with the downloaded lowlevel.ahk. Also, “1 ? 2” results in 12, which could indicate that the “?” is not treated properly.

As majkinetor showed, we can have a calculator with 9 AHK commands (after including lowlevel.ahk):
Gui Add, Edit, w300 vCode
Gui Add, Button, w0 Default gExec
Gui Add, Text, vRes x+0 w200, Result =
Gui Show, AutoSize, Evaluate AHK expression {Enter}
__init()

Exec:
   Gui Submit, NoHide
   GuiControl,,res, % "Result = " . __expr(code)
return

The double underscore prefix looks informative for me. How much lower level can a prefix be? :wink:

Edit 20071207: Actually, 3 AHK commands are enough for a calculator.
Loop {
   InputBox code,Evaluate AHK expression,% "Result = " __expr(code),,300,120,,,,,%code%
   IfEqual ErrorLevel,1, Break
}
#include lowlevel.ahk
Assign it to a hotkey, and you have a fast simple, always available calculator.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
but can you call the script _.ahk and be stdlib compliant?

I think we need some prefix here so it can be added to the stdlib. This is obviously quite powerful.

edit:
_.ahk in my lib did not work.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Oct 02 2013 02:33 PM
  • Joined: 24 May 2006
I think it should be __.ahk :)

Lexikos
  • Administrators
  • 9445 posts
  • Last active:
  • Joined: 17 Oct 2006

Ternary operators don’t work for me, either (return blank) with the downloaded lowlevel.ahk. Also, “1 ? 2” results in 12, which could indicate that the “?” is not treated properly.

As I pointed out, __expr doesn't evaluate the expression. It does, however, create a deref for "?"... :x It worked in my previous test because I forgot to call __init().

The double underscore prefix looks informative for me. How much lower level can a prefix be? :wink:

Heh, that's priceless.

but can you call the script _.ahk and be stdlib compliant?

As I said, the script is std-lib compliant if named ".ahk". AutoHotkey uses everything up to the first under-score (in this case nothing) as the prefix. Explorer won't let you give it "no name," but Firefox will. You can also use a command prompt (or AutoHotkey script!) to rename it. I only named the file lowlevel.ahk because Firefox would save ".ahk.html". :x

Edit: Script updated.

Lexikos
  • Administrators
  • 9445 posts
  • Last active:
  • Joined: 17 Oct 2006

this is amazing... thanks. while this opens many new opportunites, i am worried about the internals of AHK being discussed in public. IMHO, you could have opted the "appropriate section" of our forum. please consider. thanks again for your wonderful contribution. :)

Come again? AutoHotkey is open source. Anyone that understands my script could most likely understand the AutoHotkey source. Anyway, how could any of this be exploited? The "entry point" is RegisterCallback, which must be called by the script itself. As for the "appropriate section," what would that be? :?


The only bad thing about this is that it is 100% AHK version dependent.
Perhaps Chris can use your work to implement dynamic code internaly...

I don't imagine so. The main barrier to building in dynamic code functionality is that script objects (Line, ArgStruct, DerefType, etc.) use "persistent" memory (avoiding the overhead of dynamic memory allocations), so can't be deleted. Changing this behaviour would affect the performance (at least, memory usage) of all scripts. This isn't an issue with the script, since it allocates the structures itself. (Doing it this way in AutoHotkey itself would involve duplicating a considerable amount of code.)

Perhaps I'll take a crack at it some time, and see how it performs. For now I'm trying to focus on my many unfinished projects.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

Edit: Script updated.

Thanks! The ternary op works now in the calculator example!

corrupt
  • Members
  • 2558 posts
  • Last active: Aug 05 2014 03:20 PM
  • Joined: 29 Dec 2004
Cool :!: Thanks :D

Lexikos
  • Administrators
  • 9445 posts
  • Last active:
  • Joined: 17 Oct 2006
This morning (it's nearly 6 PM here) I started working on __findVar, with the intention of adding support for creating local variables. It got up to 55 lines before I realised what a PAIN it would be to write code to insert a local variable into the (mVar or mLazyVar) array, since I'd potentially have to reallocate mVar and/or move Vars from mLazyVar into mVar, all the while preserving alphabetical order. {gasps for air} :x

Luckily, I thought of a way to make __findVar smaller, more capable, and more efficient:
Instead of searching some function's local variable arrays, simply switch them with __findVar's arrays temporarily and perform a double-deref! Consequently, I have renamed __findVar to __getVarInContext.

__getVarInContext( sVarName [, pScopeFunc ] )
Retrieves the Var structure of a variable with the given name in the context of the specified function (or in global context if no function is specified), with the same semantics as a double-deref. pScopeFunc is a pointer to a Func, not a callback or function name.

If a thread running __getVarInContext is interrupted between the second and last RtlMoveMemory calls and __getVarInContext is called again (causing recursion), the local variables in the first instance of __getVarInContext are overwritten. This should be very rare, considering the very small "window of inopportunity."

I have also removed the "LocalVarsOnly" parameter, since it is no longer applicable.