Skip to content

Instantly share code, notes, and snippets.

@MarkVillacampa
Last active December 27, 2015 19:49
Show Gist options
  • Save MarkVillacampa/7379799 to your computer and use it in GitHub Desktop.
Save MarkVillacampa/7379799 to your computer and use it in GitHub Desktop.
Trying to add methods at runtime for the JSExport protocol in JavaScriptCore. As-is, the method is not called from Javascript. Uncomment lines 7-8 and the end of 11, comment line 65, and run. The method is now called from JavaSript. Any idea why?
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
#import <objc/runtime.h>
const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod);
//@protocol MyProtocol <JSExport>
// -(void)one:(id)one;
//@end
@interface MyClass : NSObject //<MyProtocol>
-(void)one:(id)one;
+(Protocol*)getProtocol:(NSString*)protocol;
+(void)addProtocol:(NSString*)protocol extendingProtocol:(NSString*)extends withMethods:(NSArray*)methods;
@end
@implementation MyClass : NSObject
-(void)one:(id)one
{
NSLog(@"Hello!");
}
+(void)addProtocol:(NSString*)protocol extendingProtocol:(NSString*)extends withMethods:(NSArray*)methods;
{
Protocol *prot = [self getProtocol:protocol];
if (extends != nil)
{
protocol_addProtocol(prot, objc_getProtocol([extends UTF8String]));
}
if (methods != nil)
{
for(NSString *method in methods)
{
bool instance = true;
Method m = class_getInstanceMethod(self, NSSelectorFromString(method));
if (m == nil)
{
instance = false;
m = class_getClassMethod(self, NSSelectorFromString(method));
}
const char *types = method_getTypeEncoding(m);
protocol_addMethodDescription(prot, NSSelectorFromString(method), types, true, instance);
}
}
objc_registerProtocol(prot);
class_addProtocol([self class], prot);
}
+(Protocol*)getProtocol:(NSString*)protocol
{
Protocol *prot = objc_getProtocol([protocol UTF8String]);
if (prot == nil)
{
prot = objc_allocateProtocol([protocol UTF8String]);
}
return prot;
}
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
@protocol(JSExport); // Stub needed to "see" the protocol at runtime.
[MyClass addProtocol:@"MyProtocol" extendingProtocol:@"JSExport" withMethods: @[@"one:"]];
JSContext *context = JSContext.new;
// MyClass *myclass = MyClass.class;
// context[@"Myclass"] = myclass;
// [context evaluateScript:@"Myclass.one()"];
NSLog(@"Conforms to JSExport: %d", class_conformsToProtocol(NSClassFromString(@"MyClass"), objc_getProtocol([@"JSExport" UTF8String])));
NSLog(@"Conforms to MyProtocol: %d", class_conformsToProtocol(NSClassFromString(@"MyClass"), objc_getProtocol([@"MyProtocol" UTF8String])));
uint *outCount;
protocol_copyMethodDescriptionList(objc_getProtocol([@"MyProtocol" UTF8String]), true, true, &outCount);
protocol_copyMethodDescriptionList(objc_getProtocol([@"MyProtocol" UTF8String]), true, true, &outCount);
NSLog(@"Types: %s", protocol_getMethodDescription(NSProtocolFromString(@"MyProtocol"),NSSelectorFromString(@"one:"), true, true).types);
NSLog(@"Types internal: %s", _protocol_getMethodTypeEncoding(NSProtocolFromString(@"MyProtocol"),NSSelectorFromString(@"one:"), true, true));
NSLog(@"Number of methods in MyProtocol: %i", outCount);
}
}
@interstateone
Copy link

@MarkVillacampa I commented on that bug but missed hitting "reply" so thought I'd follow up here as well. I did some investigating and it looks like this won't be something that would get changed in JSC itself because it would have bad side effects. I'll note I'm not involved with WebKit, I've only done some testing and prodding of my own. To answer your two other questions:

  1. Because using method_getTypeEncoding with dynamic protocols doesn't have the extended type encoding that _protocol_getMethodTypeEncoding does and that's needed to handle data types when moving to/from ObjC to JS (from my experience specifically with JSValue method arguments).
  2. Doesn't seem like it. It returns null for dynamic protocols because it's only using the compile-time information.

It's certainly possible to build your own JSC to put into an app though, and projects like this make that easier.

@sdegutis
Copy link

fwiw this is one of the biggest reasons I ditched JS and went with Lua for my app's extension stuff

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment