Help with the Object() function

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Andrew1802
Posts: 23
Joined: 02 Jul 2020, 07:28

Help with the Object() function

26 Nov 2021, 11:04

Hi! Hello!
So, I'm working on a msgbox function with fancy formatting options and support for array printing, long story. Anyway, I'm trying to make it parse each passed parameter into 2 things: (1) a formatting string (e.g. "0x{:016x}") and (2) a value to be formatted.
example:

Code: Select all

printf("<f hex: 0x{:016x}>" 0x68F20106242C, "<f int: {:i}>" 0x68F20106242C)
should display a msgbox containing:

Code: Select all

hex: 0x000068F20106242C
int: 115388608554028

This works just fine for regular values like strings and numbers, I use RegEx to extract the formatting string from the parameter and print the value formatted accordingly. Unfortunately, object references can't be concatenated with strings, so I can't just plop an array reference at the end of the formatting string and expect it to work.

To avoid this limitation, I have resolved to using the address-of operator to instead pass the addresses to the object reference at the end of the string and then the Object() function to get the reference from that address, for example:

Code: Select all

printf("<f 0x{:x}>" &arr1:={a:1,b:2,c:3,d:4,e:5,f:6,g:7}, "<f {:i}>" &arr2:={a:1,b:2,c:3,d:4,e:5,f:6,g:7})
would print:

Code: Select all

key(a)=0x1
key(b)=0x2
key(c)=0x3
key(d)=0x4
key(e)=0x5
key(f)=0x6
key(g)=0x7
key(a)=1
key(b)=2
key(c)=3
key(d)=4
key(e)=5
key(f)=6
key(g)=7
Okay, so where's the issue?

Well, the issue is that the Object() function will throw a critical error if it is called with a pointer which doesn't point to an object, followed by the script crashing, so I can no longer use it to print simple numeric values. I will provide a very simplified, non-variadic version of the function to illustrate this behavior:

Code: Select all

aNumber:=12341232
aString:="qwerty"
anArray:={a:1,b:2,c:3,d:4,e:5,f:6,g:7}

test(str_val){
	val:=RegExReplace(str_val, "This is a string and this is ")
	if IsObject(obj:=Object(val)){
		for k,v in obj
			out.="key(" k ")=" v "`n"
		ObjRelease(val)
	}
	else
		out:=val

	msgbox % out
}

; calling it like this works as intended:
test("This is a string and this is " &anArray)
; calling it like this works as well because strings can't be interpreted as addresses:
test("This is a string and this is " aString)
; calling it like this crashes the script with "Critical Error: Invalid memory read/write." which I wasn't able to circumvent in any way:
test("This is a string and this is " aNumber) ; this is because Object(12341232) will try to resolve an object reference at address 12341232 which is apparently a big no-no and we should immediately crash the script?
Anyway, any ideas on how to make this work? I don't want to use separate parameters for the string and value, I'm confident it can be done with parsing.
Andrew1802
Posts: 23
Joined: 02 Jul 2020, 07:28

Re: Help with the Object() function

26 Nov 2021, 13:52

Solved it. Low level hacks FTW! :D

edit: the rabbit hole continues...

Code: Select all

isNumberArrPtr(num){
	return (NumGet(NumGet(NumGet(num+0)), "Int")=0x245c8948)?1:0
}

isNumberArrPtr(12341232) ; this will throw the same critical error as Object(12341232). Looks like I'm gonna have to read the memory manually using DllCall. Not a real issue, just annoying.

image.png
All array pointers point at the same pointer within the interpreter image in memory, which in turn points to some instruction, I don't care what it does as long as it can be used to check if passed number is an array pointer. Tested on a virtual machine as well, with a different AHK version and it's solid. Might break in the future though...
image.png (294.89 KiB) Viewed 609 times
lexikos
Posts: 9621
Joined: 30 Sep 2013, 04:07
Contact:

Re: Help with the Object() function

26 Nov 2021, 21:50

Ugh.

Just don't pass an address to Object() if you don't know that it's an object.

The root of the problem is that you are combining the formatting options and value into one string parameter. Why don't you pass them as parameter pairs?

I see in your screenshot the code IsObject(obj:=Object(val)). There is never a valid way to use this code. If val is an object, then obj is an integer, not an object. If val is not an object, it is either an object address or it is invalid, in which case it must never be passed to Object(). By passing the value to Object(), you are declaring that this absolutely is an Object's address. If you pass an invalid address to Object(), it might crash the script, or it might appear to work. Maybe the program will crash when you try to use that object, or maybe it will corrupt the state of your program and cause behaviour that you would never expect.

Normally the program knows what is or is not an object, because the program retains type information when an object is created, returned, etc. The address-of operator gives you the value of the object's address, replacing the datum "this is an object" with "this is an integer". Every possible object address has the same binary value as a valid unrelated integer. Unless you know for certain the origin of an integer or have significant restrictions on the range of input values, there is no way to correctly determine whether it is an object. For instance, if you have a loop counting from the minimum integer value to the maximum integer value, for some iterations it will appear that A_Index is an object.

If I have a struct, and I put the integer 0x245c8948 into it, that struct still is not an Array.

This number you have found may work for your version and build of AutoHotkey, but will not work for every version. It probably won't even work for the 32-bit builds of the same version.

Even if you find this number at the start of an object, that does not mean the object is an Array. The first A_PtrSize bytes of each object contain the address of its method table. All objects created by {}, [], class, new SomeClass, variadic parameters, StrSplit, etc. share the same method table. There is no dedicated Array type in AutoHotkey v1.

ObjRelease(val) is also incorrect. You use ObjRelease to release a reference that the interpreter does not know is a reference. The address-of operator only gives you the address; it does not count as a reference unless you call ObjAddRef. If the object had only one reference in the original variable, it still has only one reference. obj := Object(val) creates a new reference, but that one is released automatically when the variable obj is freed. When the original variable is eventually freed, it will attempt to release its reference, which would already be invalid at that point as it would have been deleted prematurely due to the incorrect ObjRelease call.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Draken, Rohwedder and 152 guests