Brandon Horst
Brandon Horst

Reputation: 2070

NSUserAppleScriptTask returning large value never calls completionHandler

I have an Objective-C app that is using NSUserAppleScriptTask to call some Applescript. The applescript is returning a string that can vary in size.

It works fine with the string is short. However, when it gets very long, it starts failing silently. If it attempts to return a string that is 40,000 characters long, the completionHandler will never be called, and the string returned ... is never printed. No messages to the log, no messages to the console. It is just never called.

A string of 30,000 characters works fine. I'm not sure where the precise cutoff is.

Has anyone seen this before? Is there a property or configuration setting I could change to increase this limit? Or do I just need to give up on NSUserAppleScriptTask?

Code

NSURL* scriptURL = [[NSBundle mainBundle] URLForResource:@"ApplescriptHelper" withExtension:@"scpt"];
NSError* error;
NSUserAppleScriptTask* task = [[NSUserAppleScriptTask alloc] initWithURL:scriptURL error:&error];
NSLog(@"created task %@, error %@", task, error);

/* CREATE APPLE EVENT */
// taken from https://www.objc.io/issues/14-mac/sandbox-scripting/
ProcessSerialNumber psn = {0, kCurrentProcess};
NSAppleEventDescriptor *target = [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber bytes:&psn length:sizeof(ProcessSerialNumber)];

NSAppleEventDescriptor *function = [NSAppleEventDescriptor descriptorWithString:@"test"];

NSAppleEventDescriptor *event = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite eventID:kASSubroutineEvent targetDescriptor:target returnID:kAutoGenerateReturnID transactionID:kAnyTransactionID];
[event setParamDescriptor:function forKeyword:keyASSubroutineName];
/* END CREATE APPLE EVENT */

[task executeWithAppleEvent:event completionHandler:^(NSAppleEventDescriptor*resultEventDescriptor, NSError *error) {
    NSLog(@"returned %@, error %@", resultEventDescriptor, error);

    if (!resultEventDescriptor) {
        NSLog(@"%s AppleScript task error = %@", __PRETTY_FUNCTION__, error);
    } else {
        NSLog(@"success")
    }
}];

ApplescriptHelper.scpt

on test()
    set str to ""
    repeat 40000 times
        set str to str & "x"
    end repeat
    return str
end fetchMusic

Upvotes: 0

Views: 343

Answers (1)

user5323203
user5323203

Reputation: 36

This looks like an Apple bug. You should file a report.

Running your code, osascript stalls with the following call graph:

+ 2812 start  (in libdyld.dylib) + 8  [0x7fff97a205b4]
+   2812 exit  (in libsystem_c.dylib) + 55  [0x7fff9157f403]
+     2812 __cxa_finalize_ranges  (in libsystem_c.dylib) + 345  [0x7fff9157f0ff]
+       2812 std::__1::ios_base::Init::~Init()  (in libc++.1.dylib) + 16  [0x7fff8f1ebd98]
+         2812 std::__1::basic_ostream<char, std::__1::char_traits<char> >::flush()  (in libc++.1.dylib) + 68  [0x7fff8f1e2f5a]
+           2812 std::__1::__stdoutbuf<char>::sync()  (in libc++.1.dylib) + 137  [0x7fff8f1ec30d]
+             2812 fflush  (in libsystem_c.dylib) + 40  [0x7fff9155c31c]
+               2812 __sflush  (in libsystem_c.dylib) + 87  [0x7fff9155c3c0]
+                 2812 _swrite  (in libsystem_c.dylib) + 87  [0x7fff91563e96]
+                   2812 __write_nocancel  (in libsystem_kernel.dylib) + 10  [0x7fff918e57ba]

I think you're running into the pipe buffer limit of 65536 bytes.

If you play with the undocumented osascript arguments that NSUserAppleScriptTask uses, you get raw AppleEvent output with a 16-byte header & UTF-16 encoded text.

Anything over 32760 in your repeat loop causes this behavior. 32760 * 2 + 16 = 65536.

Upvotes: 2

Related Questions