Crash with Properties Referring to "me"

SD8 will crash in the following 2 scenarios (I suspect they are related). Apple’s Script Editor does not experience any issues.

SD8 build 8A19, on macOS 11.2.1 (Intel).

Scenario 1

Script library containing a property referring to me and at least 1 handler.

someLibrary.scpt:

property p: me
on f()
    return "foo"
end

someScript.scpt

use someLibrary: script "someLibrary" # Causes crash on opening the script.

Why is this a useful pattern? In complex script objects, it is sometime necessary to have a property to reference the anonymous top-level script object (see AppleScript: The Definitive Guide, 2e by Matt Neuberg). Having a such a property in the script library allows for delegation patterns.

Scenario 2

A use statement followed by setting the parent of the script.

someLibrary.scpt:

on f()
    return "foo"
end

someScript.scpt

use someLibrary: script "someLibrary"
property parent: someLibrary # Causes crash on compilation.

This is mainly useful as a means of caveman debugging when developing script libraries.

In both of these instances you have created a circular parent property relationship which AppleScript does not defend against.

In this case, by declaring property p: me, the value of p is the script object which declares property p thus creating a reference to its self. AppleScript does not defend against these circular relationships.

Script Debugger fails in this situation because it is trying to display the properties of the script object in the variables explorer and there is no way to identify a circular relationship (AppleScript generates unique ID’s for each iteration making it impossible to defend against this situation).

Here again, you are creating a circular relationship between two script objects. The script object loaded form the library has an implicit property parent: me definition within it. In the main script, declaring property parent: someLibrary creates a circular relationship between the two script objects (the main script and the script library). In this instance, AppleScript is crashing during compilation because it cannot compile this without traversing this relationship and blowing away the stack.

These fall into the category: “if it hurts, don’t do it”.

Note you can break these circular parent relationships by using a property parent: missing value statement within one the script objects. When doing this, you’ll have to access AppleScript global’s explicitly (e.g. AppleScript's newline) within the script object.

This comes up from time to time.

Mark,

I created a script library file called “someLibrary.scpt”. The only thing in the file is the text:

property p : me
on f()
return “foo”
end f

I launched script debugger 8.0 (8a19). I opened the library script “someLibrary.scpt” and pasted the function

on f()
return “foo”
end

I saved the file in "~Library:Script Libraries"and Script debugger crashed. I never got a chance to script a script call to the library and run it. This was very unexpected. No text was saved to the file. I will see if I can pin this down more specifically.

I uploaded the text of the crash report in a file called “Debugger text.txt.zip”

Bill Kopp

Debugger text.txt.zip (34.3 KB)

Warning : I messed on this bug report. All the stuff reported in this part of the part of the post is incorrect. I forgot to restart my Mac after the previous crash. After restarting this morning the problems went away. Sorry about that.


I quit SD and tried the same thing again it saved the file with no problems. I opened a new script window and entered the text:

on f()
return “foo”
end f

f()

The call returned “foo”.

I created a new script:

on f()
return “foo”
end f

repeat with loopVar from 1 to 1000000
set s to f()
end repeat

It ran with no errors. I quit script debugger and it crashed.

I uploaded the text of the crash report in a file called “Second crash.txt.zip”

I reopened Script Debugger and ran:

on f()
return “foo”
end f

repeat with loopVar from 1 to 1000000
set s to f()
end repeat

I closed the script window and reopened it. With no crash. When I ran the original

on f()
return “foo”
end f

repeat with loopVar from 1 to 1000000
set s to f()
end repeat

This time I did notice the response time on my Mac was slow. I launched “Activity Monitor” and watched the cpu process list and nothing seemed to hog too much time. I copied everything while running this test and saved it is a file called “Console log.txt”

There seems to something odd here. I’m sending this report for now and will post again if I find out anything useful. I want to see if I can figure out what is trigger the erratic behavior. But this can take while to do.

Also, on a different topic, every time I send the crash report clicking “Send” then the next time I quit and restart Script Debugger it asks me if I want to send the report. When I check the crash time it has the same exact crash time as before down to the fractions of a second. I have to click the “Discard” button on the crash reporter to get it to stop doing that. However it still has text in the crash report every time it comes up.

Bill Kopp

Second crash.txt.zip (32.9 KB)

Console log.txt.zip (2.1 KB)strong text

Thanks for the very detailed answer, Mark. Appreciate it.

Just in case it was missed, I would like to ensure you’re aware that my example scripts do compile & run just fine in Apple’s Script Editor & osascript. No crashes.

My 2nd scenario is definitely of doubtful utility, but this first scenario does let you do some powerful things with script libraries in Script Editor. A fuller example would be:

someLibrary.scpt

property customLogger : me

to logger(s)
	log s
end logger

to longHandler() -- Function that will be called from the other script.
	set handlerResult to "foo"
	customLogger's logger(handlerResult) -- Will allow the caller to modify the logging behviour.
	return handlerResult
end longHandler

someScript.scpt

use scripting additions
use someLibrary : script "someLibrary"
set someLibrary's customLogger to me -- Use this script's logger implementation.

to logger(s)
	log s & " [called from script " & my name & " on " & (current date) & "]"
end logger

someLibrary's longHandler() -- Do something useful with the library.

Running someScript will log the message foo [called from script someScript on Saturday, February 13, 2021 at 10:03:03 PM] rather than just foo.

I can definitely accept that this may be a case of “if it hurts, don’t do it”. That said, if it’s the variables explorer causing the crash in Script Debugger, I would argue that SD should display an error message rather than just outright crashing on opening/compilation.

Just to ensure I wasn’t losing my mind, and that this was valid AppleScript code, I did find the reference from AppleScript: The Definitive Guide, 2e. On p435 (search for “topLevel”):

A script object’s top-level entities can be accessed by using the name of the script object, as we know. But the script as a whole has no name.

… the solution is to give the script as a whole a name. You can do this by initializing a top-level property to the value me (see “Me” in Chapter 11).
property topLevel : me

Of course, this book predates the advent of script libraries, so I may just be out of luck. Ah well.

I can definitely accept that this may be a case of “if it hurts, don’t do it”. That said, if it’s the variables explorer causing the crash in Script Debugger, I would argue that SD should display an error message rather than just outright crashing on opening/compilation.

It is by no means easy to write software that finds every possible bug and reports all those errors. This is why post development testing called “beta testing” exists. For scripters to protect their script they need to put tests into their script to make sure certain things don’t happen. Previously in the discussion it talked about variables being the same. There are ways to test if variables are the same value or if they literally the same thing.

set A to 50
set B to 50

A = B --> returns true

(a reference to A) = (a reference to B) --> returns false

script S1
property P : 1
end script

script S2
property P : 2
end script

script S3
property P : 1
end script

(P of S1) = (P of S2) --> returns false
(P of S1) = (P of S3) --> returns true

(a reference to (P of S1)) = (a reference to (P of S3)) --> returns false
(a reference to (S1)) = (a reference to (S3)) --> returns false

(P of S1) = (P of S3) --> returns true
(P of S1) = (P of S2) --> returns false

The script above shows the values stored in both variables A & B as well as variables S1 & S2 are the same. But all 4 variables are completely different variables.

Bill Kopp

The other thing to note, is that by wrapping the “me” property in a script object, SD does not crash despite there still being an infinite hierarchy in the variables explorer.

someLibrary.scpt

script wrapper ###
	property customLogger : me
end script

to logger(s)
	log s
end logger

to longHandler()
	set handlerResult to "foo"
	wrapper's customLogger's logger(handlerResult) ###
	return handlerResult
end longHandler

someScript.scpt

use scripting additions
use someLibrary : script "someLibrary"
set someLibrary's wrapper's customLogger to me ###

to logger(s)
	log s & " [called from script " & my name & " on " & (current date) & "]"
end logger

someLibrary's longHandler()

This compiles & runs in Script Debugger. The variables explorer does show an infinite hierarchy:

It is by no means easy to write software that finds every possible bug and reports all those errors. This is why post development testing called “beta testing” exists.

Absolutely. A bug can’t be fixed until it’s identified.

This is definitely an example that pushes the boundaries of the language. If Script Debugger can’t handle it because of its debugging/explorer facilities, then the solution is to not write this type of AppleScript code. If it’s a bug in Script Debugger that can be identified & fixed, then that’s even better.

I can’t think of any way to directly guard against this code from within AppleScript, since it works fine in Script Editor, and the crash happens in Script Debugger before the AppleScript is executed.

The point I was trying to make is all development languages have strengths and weaknesses. When a language is created and a development platform is created there are always trade offs. This is why people will often use a particular programming language for a particular need. Anyone that designs a language that handles all problems well, and has no drawbacks will become very, very wealthy. When I studied computer science at UCI in California they beat students over the head with this idea. Developers need to recognize strengths and weaknesses. Programs with lots of extra features make it harder to change things without introducing lot of bugs, …

I totally agree with what you said. I’m not sure if you are calling for a change or if you are just pointing out a limitation. My thought is, I adopt different kinds of design strategies depending on what I am doing. The right kind of design strategy can eliminate the need to worry about what you pointed out.

As an example I’ve seen many scripts that do not have much error checking. One of my biggest design strategies is to put errors checks, that return info about the error, around every single AppleScript statement that can error. It might make my script 5 times longer then most other scripts doing the same thing but when it errors I know exactly where it errored and what the error was. This makes the script a lot easier and faster to debug, it does take longer to type, but in the end the entire development cycle takes less time.

Bill

I’ve been able to resolve the crash when opening scripts (your Scenario 1) by imposing an arbitrary limit on recursion in the 8A20 build. But I still caution against making script objects with properties that reference me as you are creating a circular reference can lead to infinite recursion.

Unfortunately your Scenario 2 cannot be defended against and the crash its self is happening deep within the AppleScript runtime. I realize that the Script Editor is not impacted by these sorts of problems to the same degree Script Debugger is. This is a consequence of the additional introspection of scripts done by Script Debugger to provide the functionality it does.

I had some ideas for how to possibly avoid the Scenario 2 crash but it turned into a game of whack-a-mole and I had to give up. Script Debugger must query AppleScript for information about a script’s properties following a compile and this sooner or later leads AppleScript into a crash as it tries to traverse the script’s parent path.

1 Like

One point to keep in mind is that script libraries are different from other script objects in that they are shared between all client scripts running in the same AppleScript component instance. That opens the door to all sorts of recursion and other unexpected behaviors.

1 Like

I’ve been able to resolve the crash when opening scripts (your Scenario 1) by imposing an arbitrary limit on recursion in the 8A20 build. But I still caution against making script objects with properties that reference me as you are creating a circular reference can lead to infinite recursion.

That’s great – thanks for your help with this. I’ll take your word that it’s ill-advised to use properties referencing me & try to avoid it.

Appreciate you looking into it!

Thanks Shane – wasn’t aware of that. I think recent versions of Script Debugger runs each script with a separate component instance, but that certainly does open a can of worms.

That’s right. But it still leaves open (a) the issue of the host where you deploy such a script, and (b) the unknown of what is different in the way AppleScript handles this (relatively recent) internal sharing.

1 Like