Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix "No structure encoding found for ?" exception when calling certain methods with C struct arguments/return value #110

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

rodionovd
Copy link

@rodionovd rodionovd commented Nov 3, 2023

Note
I realize that you probably don't accept unsolicited external PRs in this repo, so please feel free to treat it as an elaborate bug report.
(This one has been bugging me for years so I decided to get to the root of it, for research purposes mostly).
Thanks!

The Problem

When it comes to working with C struct arguments in Objective-C methods, Mocha (like e.g. PyObjC and other scripting environments) relies on scripting bridge support metadata found in frameworks/bundles those methods come from, like e.g. Foundation or AppKit.

Now, in order to understand which C struct type metadata it needs to look up in these bridge support definitions when calling a certain method, Mocha asks the Objective-C runtime to provide it with a full method type signature. In most cases the result contains a perfectly valid type encoding of this method's struct arguments and/or a struct return value, but in some cases these type encodings might be incorrect or bogus: while the struct fields themselves are encoded correctly, the name of the struct type is replaced with a "?":

Method method = class_getInstanceMethod(NSProcessInfo.class, @selector(operatingSystemVersion));
const char *encoding = method_getTypeEncoding(method);

// ✅ Expected: encoding == "{NSOperatingSystemVersion=qqq}16@0:8"
// ❌   Actual: encoding == "{?=qqq}16@0:8"

This missing name in a struct type encoding prevents routines like -[MOFunctionArgument getValueAsJSValueInContext:] and -[MOFunctionArgument setValueAsJSValue:context:] from converting this struct from/to a JS value, because it fails to look up this struct's definition in bridge support metadata by its bogus name.

As a result, it's impossible to call such methods from CocoaScript as doing so leads to the following runtime exception:

"No structure encoding found for ?"

Steps to Reproduce

This error is easy to trigger by running the following CocoaScript code:

NSProcessInfo.processInfo().operatingSystemVersion()

// - or -

NSProcessInfo.processInfo().isOperatingSystemAtLeastVersion({
    majorVersion: 14, minorVersion: 0, patchVersion: 0
})

// - or -

NSAffineTransform.transform().transformStruct()

// - or (going beyond the default frameworks loaded by Mocha) -

__mocha__.loadFrameworkWithName("CoreMedia")
__mocha__.loadFrameworkWithName("Vision")

VNDetectTrajectoriesRequest.new().targetFrameTime()

Merely calling the methods above leads to the "No structure encoding found for ?" exception. I'm not aware of any user-space workarounds.

Proposed Solution

We might expand _MOFunctionInvoke()'s Objective-C method branch to compare the type declarations we get from method_getTypeEncoding() / -methodSignatureForSelector: with what's in the bridge support metadata for this method and replace any bogus struct names ("?") with their actual names from the latter if found.

Implications

UX

The promise of the proposed solution above is to only fix types for structs with already bogus names, so the worst case scenario is that we replace a bogus struct name with another bogus one (in case there's something wrong with the declaration in the bridge support file) – which won't degrade the user experience a bit.

Performance

The implementation also tries not to perform any additional (to the existing code) work unless necessary, so the performance implications are likely tolerable (although I haven't run any performance stress tests).

References

I've collected a few mentions of this issue from various Sketch plugin developers throughout the years:

…ive-C methods with C struct arguments/return value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant