Last active
December 27, 2015 19:49
-
-
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?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} |
@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:
- 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). - 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.
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
Just filed a Webkit bug regarding this: https://bugs.webkit.org/show_bug.cgi?id=126121